你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【经验分享】STM32:GPIO口的使用

[复制链接]
STMCU小助手 发布时间:2022-2-6 19:49
前言
  "GPIO的使用1"中主要从内核代码开始,从寄存器的地址映射开始,对GPIO的封装和操作执行逻辑详细分析了一下;
  内核的函数接口标准是都是一样的CMSIS,了解了GPIO外设的原理,也就了解了其他外设是如何封装的;
  GPIO使用时先确定是否为外设复用;目的是确定输入输出数据是给外设处理,还是存放在GPIO寄存器里就完了;
  然后确定IO的输入输出模式;目的是通过软件配置,选择端口在芯片内部的电路连接方式;
  GPIO上电默认为浮空输入模式,禁止了上下拉电阻;上下拉电阻默认30-50kΩ;保护二极管防止电流反向击穿;
6 Q! k' Q2 {+ P- D* ?
1371863-20201107110810180-995672057.png

+ }) [' B8 H3 o# E
  0.1 输入模式
    上拉输入:使能上拉电阻的连接,断开下拉电阻的连接;
    下拉输入:断开上拉电阻的连接,使能下拉电阻的连接;
    模拟输入:将IO引脚连接至内部ADC;  
  0.2 输出模式
    推挽输出:将P-MOS管和N-MOS管以推挽方式连接;通过两个MOS管的导通与截止来输出高低电平;配置上下拉电阻不使能;
            特点是既可以消耗负载的拉电流,也可以向负载输出拉电流,开关时间快;
    开漏输出:P-MOS管始终截止,通过N-MOS管结合上下拉电阻,控制输出高低电平;配置上下拉电阻同时使能;
            特点是输出高电平的驱动能力完全由上拉电阻决定,输出低电平的驱动能力十分稳定;
  0.3 可以将多个开漏输出并连至同一个上拉电阻,形成"线与"逻辑;当其中一个开漏输出输出低电平时,相当于并联回路被导线短路;其他输出也被接到地了;
  0.4 TSM32H7IO口总的电流最大值为140mA,单个IO口的电流最大值为20mA;具体硬件参数见数据手册;
  0.5 TTL和CMOS电平标准手册可以查看安富莱论坛:http://www.armbbs.cn/forum.php?mod=viewthread&tid=87676
1 GPIO port
  STM32一共有7组GPIO port,分别是GPIOA[15:0]~GPIOG[15:0],每组GPIO port 有16个 pin;每组GPIO port都有一组寄存器;
  GPIO寄存器的控制单位是GPIO port,而不是pin;所以寄存器的最小处理单位是一个16位的字长(0xFFFF);
  至于寄存器的配置我们之后小节在解析,首先来了解一下标准库是如何将GPIO映射到地址上的;
  
1371863-20200516110102171-1093576858.png
/ p" l/ K8 d6 p
  1. /*stm32f10x.h 1408-1414行声明如下*/2 u  r( B/ h/ U& v% l* c, P
  2. #define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
    ' @4 ^3 X. t( B  r+ P
  3. #define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)3 A; b# O! I/ L
  4. #define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE); O, |5 Y# k! ~1 W
  5. #define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE); L7 h6 u0 g# l! [
  6. #define GPIOE               ((GPIO_TypeDef *) GPIOE_BASE)6 c! }' U- \: U- ?3 D8 v! D
  7. #define GPIOF               ((GPIO_TypeDef *) GPIOF_BASE)
    ; Z. k- w- M* ?* [  {2 T& q
  8. #define GPIOG               ((GPIO_TypeDef *) GPIOG_BASE)
    2 i6 b* m+ X8 F. T6 F! d3 `

  9. 2 f6 {' k' l) X
  10. /* stm32f10x.h  1001-1010行;0 W2 I# }2 i4 A; q* Z- P
  11. *把结构体的首地址映射到GPIO的首寄存器地址,就可以通过该结构体对硬件寄存器操作;
    ; I6 H! V# l4 q6 L2 S5 }+ }
  12. *结构体的地址通过结构体指针来赋值对应上*/
    % I% S) F7 U+ h( {6 s" S
  13. #define __IO  volatile /*core_cm3.h  NO.116*/: g" `+ F5 Y8 k) X
  14. typedef struct
    2 M5 A1 Y# a% g- s0 I) P
  15. {
    # z1 R3 ^( C0 x
  16.   __IO uint32_t CRL;# E" }( D4 D1 V+ ?+ z
  17.   __IO uint32_t CRH;
    ) U. `8 |- b* c9 \
  18.   __IO uint32_t IDR;9 t" t5 v/ R/ G# t, n% n
  19.   __IO uint32_t ODR;
    9 }6 z- a! e& R/ z9 n
  20.   __IO uint32_t BSRR;
    7 V( L4 w; T" r& U
  21.   __IO uint32_t BRR;' W% l5 J, ^& b' F# x0 @
  22.   __IO uint32_t LCKR;
    ! G; s8 ]' |9 u- ^! @. c
  23. } GPIO_TypeDef;
    * G# H; I: _8 U- P

  24. 0 X# f7 M; T4 \
  25. /*stm32f10x.h 1315-1321;*/
    ; M, c5 G. R) _
  26. #define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)2 A; ?$ ^4 G' X8 C9 {; G2 ?( |1 p: j
  27. #define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)
    ! S, @3 [5 z+ W8 ?9 I& G% P
  28. #define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000)
    7 e; n( o" A3 m+ Z# ~4 k0 @9 u
  29. #define GPIOD_BASE            (APB2PERIPH_BASE + 0x1400)3 m0 A! _+ v$ B9 v
  30. #define GPIOE_BASE            (APB2PERIPH_BASE + 0x1800)& r7 F4 n5 n+ ]; D
  31. #define GPIOF_BASE            (APB2PERIPH_BASE + 0x1C00)
    8 H* l  w7 h7 M. ]5 \9 z* G
  32. #define GPIOG_BASE            (APB2PERIPH_BASE + 0x2000)  ~- L- M- {! |: ]# d7 {

  33. ; L: K' |4 O1 `0 M/ e, Q3 v
  34. /*stm32f10x.h 1282-1283; GPIO都属于APB2总线,使用的时候要使能APB2总线的时钟源;*/
    5 h1 y1 B+ f1 L+ F3 g# Q
  35. #define APB1PERIPH_BASE       PERIPH_BASE' S2 M5 w! r$ x  b
  36. #define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)
    % ^1 V/ n# }1 M4 M2 ?# W/ Y) ]: G2 `
  37. $ o% _/ e8 b9 v' g
  38. /*stm32f10x.h 1274*/$ a0 T0 W0 O: E6 i
  39. #define PERIPH_BASE           ((uint32_t)0x40000000)
复制代码
, l, D, R+ T# v* J: @
2 GPIO 寄存器
  GPIO涉及的寄存器较多,复用功能和重映射功能都需要配置专门的AFIO寄存器,但是本节暂时没有涉及;
  本节主要介绍了通用GPIO口涉及到的7个寄存器,具体寄存器的说明和使用如下;
  2.1 CRL端口配置低寄存器,CRH端口配置高寄存器 control register low,control register high;
    CRL和CRH都是32位寄存器,如下图所示,一起用来控制该组GPIO port的16个引脚配置;
. e; I( E" \6 h& j3 Y
1371863-20200515102909983-458893530.png
   
    2.1.1 CRL和CRH寄存器复位值为0x4444_4444;CRL偏移地址:0x00,CRH偏移地址:0x04;配置信息封装如下;
  1. //定义了CRH和CRL寄存器需要的参数;以下声明在stm32f10x_gpio.h的前200行;
    * v- W4 ~) d9 y+ y! i
  2. typedef struct: i* G" t6 q1 Z+ P/ n
  3. {
    * @  {+ w9 P. w( L( u/ t& k( U
  4.   uint16_t GPIO_Pin;                /*用16位bit的每一位分别表示一个引脚*/            ) ?9 I: h; ~" _0 f+ _2 B7 h
  5.   GPIOSpeed_TypeDef GPIO_Speed;     /*用2位bit来表示输出模式的最大速度*/
    1 W; q! h, F0 ^; J3 Y1 }: b
  6.   GPIOMode_TypeDef GPIO_Mode;       /*CNF MODE,具体见结构体*/
    8 i% z' B% v  I6 Y) ~) _
  7. }GPIO_InitTypeDef;
    % E$ b8 ]! `0 l5 n5 @7 T/ N
  8. . d' a9 h$ z. B' r
  9. #define GPIO_Pin_0                 ((uint16_t)0x0001)  /*0000 0000 0000 0001b*/
    ! Z& @- H1 p9 l; g
  10. #define GPIO_Pin_1                 ((uint16_t)0x0002)  /*0000 0000 0000 0010b*/+ \" C% z1 [2 N; x" b0 u
  11. #define GPIO_Pin_2                 ((uint16_t)0x0004)  /*0000 0000 0000 0100b*/
    9 [; C$ f+ F$ ]' R# Q7 u6 C8 Q
  12. #define GPIO_Pin_3                 ((uint16_t)0x0008)  /*0000 0000 0000 1000b*/
    2 O! Y$ ^5 c& a; K& J6 j/ [; W
  13. #define GPIO_Pin_4                 ((uint16_t)0x0010)  /*0000 0000 0001 0000b*/
    # E; Q! _; X4 @4 L" t) d4 x
  14. #define GPIO_Pin_5                 ((uint16_t)0x0020)  /*0000 0000 0010 0000b*/& L( e* D' G: r- q
  15. #define GPIO_Pin_6                 ((uint16_t)0x0040)  /*0000 0000 0100 0000b*/9 |% R5 {& d, ~! L
  16. #define GPIO_Pin_7                 ((uint16_t)0x0080)  /*0000 0000 1000 0000b*/
    # P( x$ \3 a, _0 |9 O- }' I8 t# \
  17. #define GPIO_Pin_8                 ((uint16_t)0x0100)  /*0000 0001 0000 0000b*/
    ; q& A& @  V$ s+ t; d: c
  18. #define GPIO_Pin_9                 ((uint16_t)0x0200)  /*!< Pin 9 selected */5 R" n, y% L) i) x- N" m
  19. #define GPIO_Pin_10                ((uint16_t)0x0400)  /*!< Pin 10 selected */
    . Z7 W9 m0 B  r6 U2 O
  20. #define GPIO_Pin_11                ((uint16_t)0x0800)  /*!< Pin 11 selected */
    / [' h# _; V& p0 W
  21. #define GPIO_Pin_12                ((uint16_t)0x1000)  /*!< Pin 12 selected */
    3 Q2 ^0 X( {. S$ l, J3 y5 F. z$ b
  22. #define GPIO_Pin_13                ((uint16_t)0x2000)  /*!< Pin 13 selected */% A7 J! l* z/ x+ m* @2 @( L
  23. #define GPIO_Pin_14                ((uint16_t)0x4000)  /*!< Pin 14 selected */
    * T6 N/ H, r7 \8 g; p  B" W) }
  24. #define GPIO_Pin_15                ((uint16_t)0x8000)  /*!< Pin 15 selected */2 ?/ Q4 X7 d+ {
  25. #define GPIO_Pin_All               ((uint16_t)0xFFFF)  /*!< All pins selected */
    # R' Q$ @1 Z( h" F
  26. #define IS_GPIO_PIN(PIN) ((((PIN) & (uint16_t)0x00) == 0x00) && ((PIN) != (uint16_t)0x00))
    8 @# K6 A2 Q7 W

  27. 1 p$ I/ b' Y! w& `1 Q
  28. typedef enum
    . S2 g# C$ p" K7 F: u
  29. { 6 S! }+ V; Q( y8 k7 B
  30.   GPIO_Speed_10MHz = 1,     /*output MODE[1:0]*/" n% k( g2 D* R& ]. u. m
  31.   GPIO_Speed_2MHz,          /*output MODE[1:0]*/
    # K- x. E2 F) x2 r/ E2 u
  32.   GPIO_Speed_50MHz          /*output MODE[1:0]*/
    $ j" L' i& R2 Z& R( c: K" n! l
  33. }GPIOSpeed_TypeDef;4 H- L2 J# V. i, V" m: S# w4 d+ |2 o
  34. #define IS_GPIO_SPEED(SPEED) (((SPEED) == GPIO_Speed_10MHz) || ((SPEED) == GPIO_Speed_2MHz) || \
    " P5 b, `: ^5 n6 h2 b2 m
  35.                               ((SPEED) == GPIO_Speed_50MHz))2 d* l7 M+ ~( z: e. j
  36.                               
    6 \  @5 x+ y- u: s9 y& z
  37. typedef enum) f: z# j+ w+ G! c0 a1 |8 m
  38. { GPIO_Mode_AIN = 0x0,                /*0000 0000b [4]0 input [3:0]CNF+MODE*/7 `" {7 j( r8 k4 i+ A
  39.   GPIO_Mode_IN_FLOATING = 0x04,       /*0000 0100b [4]0 input [3:0]CNF+MODE*/
    9 L; [( G' R8 W4 u5 h5 q
  40.   GPIO_Mode_IPD = 0x28,               /*0010 1000b [4]0 input [5]下拉,[3:0]CNF+MODE*/
    9 \7 w% o) f+ y7 T0 Q
  41.   GPIO_Mode_IPU = 0x48,               /*0100 1000b [4]0 input [6]上拉,[3:0]CNF+MODE*/6 ?! \& e3 q; ]/ Z+ W
  42.   GPIO_Mode_Out_OD = 0x14,            /*0001 0100b [4]1 output,[3:2]CNF*/
    : `, y, v' R6 J! r3 K3 T
  43.   GPIO_Mode_Out_PP = 0x10,            /*0001 0000b [4]1 output,[3:2]CNF*/
    ( \9 q+ x0 ?8 D" H0 @9 _+ ?! `8 }( \
  44.   GPIO_Mode_AF_OD = 0x1C,             /*0001 1100b [4]1 output,[3:2]CNF*/
    / H. A- H$ n! `6 i
  45.   GPIO_Mode_AF_PP = 0x18              /*0001 1000b [4]1 output,[3:2]CNF*/
    ) W( Z: n$ c2 v% W; ~
  46. }GPIOMode_TypeDef; % T3 B5 O, r7 t
  47. #define IS_GPIO_MODE(MODE) (((MODE) == GPIO_Mode_AIN) || ((MODE) == GPIO_Mode_IN_FLOATING) || \) B2 O6 @3 x3 h1 \" d
  48.                             ((MODE) == GPIO_Mode_IPD) || ((MODE) == GPIO_Mode_IPU) || \% B. d8 l; b& V' M
  49.                             ((MODE) == GPIO_Mode_Out_OD) || ((MODE) == GPIO_Mode_Out_PP) || \
    2 }* P- ]4 O2 i6 c* s# r3 D8 ?
  50.                             ((MODE) == GPIO_Mode_AF_OD) || ((MODE) == GPIO_Mode_AF_PP)), e  g2 z  g( d" j4 Q% i$ p! H( t! [
  51. #define IS_GET_GPIO_PIN(PIN) (((PIN) == GPIO_Pin_0) || ((PIN) == GPIO_Pin_1) ||((PIN) == GPIO_Pin_2) || \5 k+ J) ~& T" |
  52.                               ((PIN) == GPIO_Pin_3) || ((PIN) == GPIO_Pin_4) || ((PIN) == GPIO_Pin_5) || \
    0 Y8 X% S& Z' r# n, W
  53.                               ((PIN) == GPIO_Pin_6) || ((PIN) == GPIO_Pin_7) || ((PIN) == GPIO_Pin_8) || \3 L$ ^. l+ r! N/ y' |1 H0 ]
  54.                               ((PIN) == GPIO_Pin_9) || ((PIN) == GPIO_Pin_10) || ((PIN) == GPIO_Pin_11) || \
    6 C! J. a5 z7 w% G' J+ I% z7 w' z+ k
  55.                               ((PIN) == GPIO_Pin_12) ||((PIN) == GPIO_Pin_13) || ((PIN) == GPIO_Pin_14) || \  V3 Q3 E8 O& f( o( ^. u
  56.                               ((PIN) == GPIO_Pin_15))
    0 {9 l. t: y; @

  57. - ?7 E# n1 j8 q1 K3 |. Z- Q
  58. typedef enum- C$ x7 l) d" n3 \. W! {0 E; A- F; Y
  59. { Bit_RESET = 0,
    8 O# {3 F# m, |4 b3 h
  60.   Bit_SET* A- x* ?1 ?( Y8 \2 b
  61. }BitAction;
复制代码

, I" C# _3 d; p
    2.1.2 配置CRL和CRH的初始化代码如下;
  1. /*以下代码位于stm32f10x_gpio.c中,配置相应port的CRL和CRH*/# f8 _3 A" F5 g7 Q* [! O8 w7 Z
  2. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)  V' S6 o! q6 m3 Y2 ?
  3. {
    - M, |) H0 ]. \0 Y, Z6 X
  4.   uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;# U5 ]5 j1 I) G5 O- K& Q
  5.   uint32_t tmpreg = 0x00, pinmask = 0x00;
    & m+ _4 y# l" ~# B8 }# v1 @3 P
  6.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));5 W  Q4 Q! l) @$ |$ S
  7.   assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));( P) W* n; S8 S' |' i9 O
  8.   assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));  8 h+ U# P( Y& O8 J" h) n
  9.   , i1 h/ g7 T# t1 e  \
  10.   currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F); //currentmode保留了GPIO_Mode[3:0];
    1 \7 ]9 G/ m' H* R* I& I& S: h
  11. : f! n) @- Q1 d& G' S( }6 R7 m- q
  12.   if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)//如果GPIO_Mode[4]为1,表示为输出模式;8 x' w9 z  l7 e
  13.   {
    : W- M" I: H4 x5 x
  14.      /*if(输出模式),将CNF[1:0]和MODE[1:0]的信息保存到currentmode[3:0]*/
    ) x& F% X# O2 t! V
  15.     assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
    4 G/ _2 {1 H# P  x' |  D9 k) F
  16.     currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;//currentmode或上GPIO_Speed[1:0];
    & z# d- k( _9 T5 t  j* g4 W
  17.   }
    2 \6 o$ P. V: k2 s
  18.   0 ^& K+ m* S& r5 S/ o
  19.   /*以下部分为CRL Configuration*/3 Y) m/ b$ U# s' [4 W$ T! Z
  20.   if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)        //if(是低8位pin);/ s% [' `2 V7 |5 u0 T+ D, ]
  21.   {
    : V- T" f( @; E3 \
  22.     tmpreg = GPIOx->CRL;                                //temreg存放CRL寄存器的信息;
    : E  W  i2 A4 {
  23.     for (pinpos = 0x00; pinpos < 0x08; pinpos++)        //pinpos为几,表示引脚几;        - S+ E1 N& G; t3 }
  24.     {( I+ }5 R: `, X+ a6 U
  25.       pos = ((uint32_t)0x01) << pinpos;                    
    8 e3 s/ q6 ?. O: h+ r* {
  26.       currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;    //currentpin:要么为当前pin值,要么为0;# K  K6 I* `! \6 M% g! g, m3 a! F1 c
  27.       if (currentpin == pos)
    8 S8 l: r  H8 w2 p2 t0 ?) B
  28.       {
    1 A7 m& u! P  i+ W/ e
  29.         pos = pinpos << 2;                                //pos:引脚对应的CRL配置位" S4 g% ^& U0 u5 R1 I$ L! v; N
  30.         pinmask = ((uint32_t)0x0F) << pos;                //pinmask:引脚对应的CRL[3:0]置18 m. q- @$ P, K2 K
  31.         tmpreg &= ~pinmask;                                //temreg中对应引脚的[3:0]清05 Y( l$ R8 B& X4 r
  32.         tmpreg |= (currentmode << pos);                    //temreg中对应引脚的[3:0]配置成currentmode[3:0]6 b! Z1 e- j) y. c4 Z5 s$ ?
  33. 6 ]" u4 U5 n& g( H" x5 E( Y
  34.         //此处的if else应该是通过ODR来配置硬件,由中文参考手册8.1.7原理图推测可知       7 w& r. P- d; \5 E8 n1 i
  35.         if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD) //if(输入连下拉电阻)" P3 K4 Q5 q  ?3 J$ R
  36.         {8 j8 a- r( R4 G7 R# ^, A6 w+ D; t3 t
  37.           GPIOx->BRR = (((uint32_t)0x01) << pinpos);     //通过配置ODR的16bit,对应pin的bit置0,连接下拉电阻;5 J- w6 |3 g2 d0 u( K7 d
  38.         }
    0 G5 L2 w% s0 X* Y" O( M, r1 ?" O
  39.         else
    : S: t( W4 }8 o) ]
  40.         {
    2 ?* |  c) j% J& N% s
  41.           if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)//if(输入连上拉电阻)0 Y6 \+ i  g; R
  42.           {; U* J' P% _' i3 u3 ]0 e
  43.             GPIOx->BSRR = (((uint32_t)0x01) << pinpos);     //通过配置ODR的16bit,对应pin的bit置1,连接上拉电阻;1 C$ ?0 z8 O) u9 U9 o
  44.           }/ g$ j( j! ]1 G4 n! C% p6 @
  45.         }0 l9 B% C3 C" y3 v
  46.       }: W6 J% V1 ]+ F. O+ c
  47.     }
    ! I" z$ R$ T+ t* i% t
  48.     GPIOx->CRL = tmpreg;  //把配置好对应pin脚的temreg放回CRL中
    ) k! U* f, y6 a1 W% R
  49.   }
    7 N, W( m: j2 S( s8 L1 A
  50.   
    3 G7 u# i- k* I% w# S" _
  51. /*---------------------------- GPIO CRH Configuration ------------------------*/- D' W& n! g% H' H4 R# p9 u
  52.   /* Configure the eight high port pins */, V/ Q; l: t) r$ T" @
  53.   if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
    8 c0 M+ Y- h) l/ `6 r
  54.   {
      B; r! z6 V. R0 k
  55.     tmpreg = GPIOx->CRH;
    $ a! U1 y+ `) Z- f" s) x
  56.     for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    ! a% J8 g( d! a/ t1 o$ t3 K1 {' d
  57.     {
    1 [5 ?! B4 |6 G3 c
  58.       pos = (((uint32_t)0x01) << (pinpos + 0x08));9 t6 |7 M, P( A) z4 v/ C
  59.       /* Get the port pins position */& K7 c! D! `7 w' Q5 w; C! U
  60.       currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);4 p' q6 y8 h9 L3 k6 \
  61.       if (currentpin == pos)* n( A+ t) L; O6 K( F' q
  62.       {
    : R( [$ S3 M2 k# M" L9 H
  63.         pos = pinpos << 2;
    - [+ V2 |# M1 Y' f5 {* @' K
  64.         /* Clear the corresponding high control register bits */
    ; ^1 O! Q7 ]! i8 a3 I7 r
  65.         pinmask = ((uint32_t)0x0F) << pos;+ W9 [, O1 v: r7 Y% \2 q0 U
  66.         tmpreg &= ~pinmask;
    3 T  o3 F/ f0 N0 y2 H
  67.         /* Write the mode configuration in the corresponding bits */
    ( \7 M+ p8 w6 Z% H& A5 J
  68.         tmpreg |= (currentmode << pos);
    - V8 _. |" d8 G" C1 M
  69.         /* Reset the corresponding ODR bit */
    : `; K+ C+ W3 E- z
  70.         if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)& U8 J2 H3 [) b; q. d
  71.         {
    * z9 c  n0 X* o6 q+ _
  72.           GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
    2 ?6 V7 A* s. ^+ N1 ~, R
  73.         }
    ; D! o0 \+ M' C" L$ d
  74.         /* Set the corresponding ODR bit */
    : J, {8 b4 A8 ?" W' l1 h7 n8 u
  75.         if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
    " O& @+ i6 q) `2 u4 @* S5 z8 t
  76.         {
    . C8 X3 ~/ V0 Q
  77.           GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));% h1 j" p  v2 t0 l
  78.         }" \5 ~- a' n( F$ _
  79.       }
    . L1 A0 o; c6 c& @7 K' q% }) z
  80.     }5 Q+ B: \+ j- B$ C
  81.     GPIOx->CRH = tmpreg;
    8 h+ U  C5 h) Q4 [) N
  82.   }
    + V9 d) M2 J6 ]9 U: K' `2 P
  83. }
复制代码

9 t3 J) F% V, J: ~2 r
    2.1.3 推挽输出,开漏输出,上拉输入和下拉输入的原理推荐参考安富莱文档的解释
  2.2 LCKR端口配置锁定寄存器:lock register
    复位值为0x0000_0000;偏移地址:0x18;
    用来锁存对应端口的CRL,CRH寄存器的配置;修改完LCK[15:0]然后锁定[LCKK],对应的CRL,CRH配置将会持续到下次系统复位信号来临;
, @1 f5 ~! Q6 v$ d8 R
1371863-20200515135452426-1739367378.png
    
  2.3 IDR端口输入数据寄存器,ODR端口输出数据寄存器:input data register,output data register;
    复位值为0x0000_0000;IDR偏移地址:0x08;ODR偏移地址:0x0C
    对于输入数据而言,每个APB2时钟会采样I/O脚上的数据存入数据寄存器中,对寄存器的读取可以获得输入数据;
    对于输出数据而言,应该也是通过APB2时钟控制,把数据放入ODR即可;
% x. Q2 t7 ^  R' S
1371863-20200515110114147-1808242956.png
 
    2.3.1 IDR寄存器的使用函数
  1. /*以下代码位于stm32f10x_gpio.c中*1 读取IDR寄存器某一位的值,即pin值;*2 读取IDR寄存器的值,即port值;*/
    0 D( V6 a9 k# n/ c, d
  2. uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
    9 ~6 ?: P9 ~8 j+ N: w- a; j$ C" f; V1 o
  3. {
    , C8 h3 Y+ V9 H
  4.   uint8_t bitstatus = 0x00;8 d( y+ t/ w+ |  G
  5.   . m6 C% T, s- s- p
  6.   /* Check the parameters */' ~7 a; j/ l* Z3 B
  7.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
    7 Y/ M( f: S+ {$ l( g
  8.   assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
    5 H6 J8 ^4 W7 _, r+ ^7 O
  9. , F8 J3 O; {9 }* u, y/ L
  10.   /*先读取整个IDR寄存器,然后通过&来读取bit,IDR寄存器的最小处理单位是16bit*/6 C5 l0 E+ f( C/ ?0 l
  11.   if ((GPIOx->IDR & GPIO_Pin) != (uint32_t)Bit_RESET)9 D& y8 _4 s( z
  12.   {3 g; j, a5 _( t3 A1 U
  13.     bitstatus = (uint8_t)Bit_SET;
    6 ]+ {, E% a- S% E! Y; m
  14.   }
    2 I2 k6 ~! I: |" U% G- A$ u
  15.   else% X4 l7 I( K; _
  16.   {1 }5 u& r+ v  q: I
  17.     bitstatus = (uint8_t)Bit_RESET;
    % H( A) }3 \6 a& P" a8 H; ^; e( m
  18.   }% }: }) q$ P! s7 y) M# x0 n0 e1 @. J4 F
  19.   return bitstatus;
      h- N: \/ ]' m3 k% m; f: v
  20. }# ]& {, _6 d3 t' a4 |
  21. ' v1 P7 W* ~' B% `3 }' M7 y- I4 C9 I
  22. uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
    / \) T4 Y$ ~# x, i1 V+ C: a: @
  23. {
    . B6 [0 z" Z& }6 A
  24.   /* Check the parameters *// P9 T( F2 E' l" q  k# Q1 ^
  25.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));; Z+ i1 h' j7 k0 A* j( d& M
  26.   
    ' J1 c: J: U- N5 Y' l! w8 x
  27.   return ((uint16_t)GPIOx->IDR);
    ! P1 J+ W7 K* U( V1 B* f
  28. }
复制代码
    2.3.2 ODR寄存器的使用函数
  1. /*以下代码位于stm32f10x_gpio.c中;
    2 y: |+ ?" L! G, x8 ?
  2. *1 读取ODR寄存器某一位的值;即读取pin值;& I6 \9 U% @1 [$ X) Y5 k- b
  3. *2 读取ODR寄存器的值;即读取port值;
    9 h2 D+ L) L# @* }3 c
  4. *3 向ODR寄存器写入port值;*/- S: D: T2 d4 a8 Z. D1 `
  5. uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
    . {3 g2 g8 f  o( J2 `$ w
  6. {
    % ^  z3 Q+ V' f/ [+ R# {. c
  7.   uint8_t bitstatus = 0x00;6 u+ e1 P. J: {1 v; g4 m; X
  8.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));1 E. Q' {8 A. T- w; q6 X- {9 u
  9.   assert_param(IS_GET_GPIO_PIN(GPIO_Pin)); 1 ?5 a4 l0 P# q  {+ ]- y5 S
  10. 2 }, f' |3 X7 A( c& K+ L7 s0 S% ?
  11.    /*先读取整个ODR寄存器,然后通过&来读取bit,ODR寄存器的最小处理单位是16bit*/
    & C6 G$ ^* L7 u3 b( _
  12.   if ((GPIOx->ODR & GPIO_Pin) != (uint32_t)Bit_RESET){3 t3 V+ J$ u) z& l+ V
  13.     bitstatus = (uint8_t)Bit_SET;" l- }+ g) ~1 X' {3 f
  14.   }6 G( Z6 |3 c3 N& W2 {
  15.   else{
    * Y; g3 B! T! n
  16.     bitstatus = (uint8_t)Bit_RESET;
    * g9 r! R: p. e5 E* |9 e8 q9 Q
  17.   }( c3 {# S/ D" }
  18.   return bitstatus;7 e! W! @& Z8 z
  19. }
    " |0 {! n* r& @' O0 t9 h

  20. : E$ R3 L# ^( T0 V9 b
  21. uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)! r& s8 ?) U, C# U8 ^
  22. {9 y" ~- M9 J9 F+ \. w: p$ I+ g: P) r
  23.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));   
    - E1 o' M$ g% P
  24.   return ((uint16_t)GPIOx->ODR);" Y: I/ a6 b% Y/ Q& \: z
  25. }. j$ V5 [- @% E

  26. - G8 v' D; G8 O+ D0 b/ Q" u
  27. void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)5 G  L8 y$ O! N! i
  28. {
    , U. ~2 v! v+ m& Q: h$ E
  29.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));+ j: e% o: ]$ ?; A
  30.   GPIOx->ODR = PortVal;8 P/ }  ?( U9 M2 s* O3 Q
  31. }
复制代码
  2.4 BSRR端口置位/复位寄存器,BRR端口复位寄存器:bit set/reset register,bit reset register;
    复位值为0x0000_0000;BSRR偏移地址:0x10;BRR偏移地址:0x14;
    对BSRR,BRR的操作,就是对该组GPIO口的ODR寄存器寄存器的操作;
) L* J: W' I8 S# q
1371863-20200515113753656-753752303.png
    
    2.4.1 BSRR寄存器:pin置1
  1. void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
    " ]4 a7 v0 Q$ B7 W
  2. {
    + t, r" H- Z2 X
  3.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
    4 @8 b0 O0 q' _
  4.   assert_param(IS_GPIO_PIN(GPIO_Pin));
    $ a% P3 }/ x' ^6 M5 m" I. e
  5.   
    7 C$ M+ O% d" J+ _
  6.   GPIOx->BSRR = GPIO_Pin;5 M  q1 e; v- B0 S2 s
  7. }
复制代码

% N8 C: w% S1 I+ }, `2 k* b$ @# W
    2.4.2 BRR寄存器:pin清0
  1. void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
    7 v( f: o. Z, r9 z3 e
  2. {$ F2 h9 f+ r. c2 \9 @
  3.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));% }( c8 ]1 F/ k
  4.   assert_param(IS_GPIO_PIN(GPIO_Pin));
    : s) C0 L- E7 {: G; q
  5.   % C: ]6 Y- Q0 q, N- T
  6.   GPIOx->BRR = GPIO_Pin;
    - M' q4 r3 C& D# `3 o* P1 Q
  7. }
复制代码
0 z9 ^7 c( @7 i
    2.4.3 设置pin脚相应的数值:pin的置1清0
  1. void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
    3 [" ~% j* Y9 Q
  2. {
    6 ~% {* d% R- _# f
  3.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));3 a$ S" @3 t' F/ F' ^3 c+ k
  4.   assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
    # _7 _1 G, d6 Z+ ?
  5.   assert_param(IS_GPIO_BIT_ACTION(BitVal)); + Z/ v5 f% ^& }: N  f
  6.   % c  y/ b7 q$ p% S; l
  7.   if (BitVal != Bit_RESET)
    3 g0 x. }! M3 m: E$ C) D
  8.   {
    ! M6 V  b; v$ Y$ p3 L  l/ c
  9.     GPIOx->BSRR = GPIO_Pin;% l2 D1 T0 T; U  x2 k( n  e
  10.   }  \3 ~( v% A6 o  D* d5 O7 o; f2 M- L
  11.   else1 o. d  r6 Y. X8 R+ ?/ v6 ]
  12.   {
    4 F# h8 Q. P5 C% ^/ ~7 z. r
  13.     GPIOx->BRR = GPIO_Pin;
    2 W- r: c! Q7 |; T- ^! T) U
  14.   }2 |9 Y) a* @4 ~4 y0 V( }6 j; v+ s
  15. }
复制代码
$ J+ U' m3 A, v2 x( n
  虽然BSRR和BRR都可以用来设置单独bit位,但是它们也是通过16bit长度来设置的,不要被函数具有迷惑性的名字给欺骗;
3 GPIO的复用和重映射寄存器
  3.1 GPIO的复用
    复用功能具体见中文参考手册8.1.11小节;
    看起来只要把输入输出模式配置成相应的外设模式,就可以使用外设了;
    看起来好像没有区分通用GPIO模式和复用GPIO模式啊,而且有的引脚有两三个复用功能的,感觉结构比较琐碎;;
  3.2 GPIO的重映射
    重映射功能具体见<中文参考手册>8.3和8.4小节;
    8.3小节主要是列出了外设的重映射引脚;8.4小节是外设的重映射寄存器的配置;
    3.1.1 外设的重映射功能需要通过AFIO_MAPR寄存器来映射到GPIO口;然后配置GPIO口复用该外设;这时候就可以使用该外设了;
    3.1.2 芯片为GPIO的复用外设提供了16个可设置的中断,对应16个引脚号;
      AFIO_EXTICRx中断寄存器配置端口号;EXTICR1配置引脚[3:0]的端口号,EXTICR4配置引脚[15:12]的端口号;
    3.1.3 外设应该有外设自己的中断函数的吧,这里为什么又为复用的外设提供了16个中断呢?这些中断对应哪些中断处理函数呢?
  3.3 位带操作
    另外标准库还为GPIO口提供了位带操作,主要就是有两个区域地址块的寄存器是可以直接以bit为单位进行设置;
    相当于给GPIO开了个小灶方便某些不想使用标准库的人可以直接设置寄存器,个人不太中意这个功能;
4 通用GPIO的示例代码
  配置GPIO端口,在寄存器层面来说:首先使能所在外设的时钟源,然后配置完CRL和CRH后GPIO口就可以使用了;
  如果GPIO pin 配置成了输入引脚,则从IDR读取数据即可;
  如果GPIO pin 配置成了输出引脚,输出的信号可以直接写入ODR寄存器,也可以通过BSRR和BRR来设置;
  1. #include "delay.h"
    & E. f% t2 |: W" l1 t

  2. ( P0 q( A" f  P1 ]
  3. int main(void)
    % E5 t' F+ X' M) q; [
  4. { # r, E( j) a; x+ M" I. J
  5.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);- i) p9 K3 J: ^8 z* A& {$ }5 y* `
  6.     GPIO_InitTypeDef GPIO_Struct;
    . u4 j! z1 N' i. o8 `

  7. : }) l& d2 z7 Q
  8.     //portA pin1 as input;     9 T2 p* x; @6 m
  9.     GPIO_Struct.GPIO_Pin = GPIO_Pin_1;             & Q. z# @2 Q& a: s3 Z# t6 R
  10.     GPIO_Struct.GPIO_Mode =GPIO_Mode_IPD ;     1 m/ Y# d! F9 V9 p/ Z# Y
  11.     GPIO_Init(GPIOA, &GPIO_Struct); ) S- W; M2 S% h  {$ L0 M
  12.    
    5 V: [7 ]: r0 b
  13.     //portA pin2 as output;                        
    9 u$ k* y- |- Z* A: t
  14.     GPIO_Struct.GPIO_Pin = GPIO_Pin_2; ) ~! s/ S7 g0 u. N# m- x0 i
  15.     GPIO_Struct.GPIO_Speed = GPIO_Speed_50MHz;6 |" }6 B) ^! n8 ]
  16.     GPIO_Struct.GPIO_Mode = GPIO_Mode_Out_PP;
    0 v. |: g( M' @
  17.     GPIO_Init(GPIOA, &GPIO_Struct);                                          ) L* C( E& G! Y( y) R
  18.     while(1)
    7 y- u9 S4 q9 ?- j7 u
  19.     {
    7 q4 E$ q# c' B1 [" I( F
  20.         GPIO_SetBits(GPIOA,GPIO_Pin_2);
    . k" e4 F, e% `; Z1 h
  21.         delay_ms(100);5 w4 C" f, c$ O  v
  22.         GPIO_ResetBits(GPIOA,GPIO_Pin_2);
      l+ H) z8 F; s6 c: F/ g
  23.         delay_ms(100);
    7 Q- Q+ i- }& b6 I8 {- d
  24.     } 6 a" O, X4 u9 r2 z
  25. }
复制代码
0 F/ K# I7 n! y+ f/ Z  ^
5 小结
  本文主要是结合通用GPIO口的寄存器,对标准库中通用GPIO口的代码进行了分析和概括;
  然后提供了一个代码示例;如果只是使用而不想理解标准库原理,则直接使用接口函数即可,如代码示例所示;

0 x5 X8 j( L2 X6 u) `
收藏 评论0 发布时间:2022-2-6 19:49

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版