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

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

[复制链接]
STMCU小助手 发布时间:2022-2-6 19:49
前言
  "GPIO的使用1"中主要从内核代码开始,从寄存器的地址映射开始,对GPIO的封装和操作执行逻辑详细分析了一下;
  内核的函数接口标准是都是一样的CMSIS,了解了GPIO外设的原理,也就了解了其他外设是如何封装的;
  GPIO使用时先确定是否为外设复用;目的是确定输入输出数据是给外设处理,还是存放在GPIO寄存器里就完了;
  然后确定IO的输入输出模式;目的是通过软件配置,选择端口在芯片内部的电路连接方式;
  GPIO上电默认为浮空输入模式,禁止了上下拉电阻;上下拉电阻默认30-50kΩ;保护二极管防止电流反向击穿;
" g& X% k9 ]( `6 H7 ]9 u4 {# V
1371863-20201107110810180-995672057.png
2 h) c/ b$ ~' \! v. R+ J% o
  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
4 g1 E- F. m- A6 P7 x; o7 ^( C4 V% n
  1. /*stm32f10x.h 1408-1414行声明如下*/
    2 Q" R: i* q( e, P$ t
  2. #define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE), s& ~" @8 g" D4 f: p  h/ Y
  3. #define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)3 q9 z, K3 ]8 v5 a* i& y
  4. #define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE)) a2 i; S: n  J
  5. #define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE)
    & ], U$ s6 J+ g; T% u- k3 \, A
  6. #define GPIOE               ((GPIO_TypeDef *) GPIOE_BASE)
    % M' H; q& s" R  p0 k1 r
  7. #define GPIOF               ((GPIO_TypeDef *) GPIOF_BASE)3 b1 }" }  N8 B$ C
  8. #define GPIOG               ((GPIO_TypeDef *) GPIOG_BASE)1 W/ T8 l1 l, Q
  9. 3 G0 t, e# _) {) h% I
  10. /* stm32f10x.h  1001-1010行;* m3 H( {! K0 K* o% d& X
  11. *把结构体的首地址映射到GPIO的首寄存器地址,就可以通过该结构体对硬件寄存器操作;; g7 |" e- k0 S  _, o. w/ u
  12. *结构体的地址通过结构体指针来赋值对应上*/
    - X: Y) E2 T4 _
  13. #define __IO  volatile /*core_cm3.h  NO.116*/
    $ U- ^+ W2 n' C5 n$ h- J
  14. typedef struct( @2 }9 j% v' H4 {5 B8 O, q  s
  15. {
    0 t2 P1 B4 E1 D2 m
  16.   __IO uint32_t CRL;5 K2 @9 J) `* I. }
  17.   __IO uint32_t CRH;
    . f* x& b2 m% R! }
  18.   __IO uint32_t IDR;' I  b& \  l; A8 T
  19.   __IO uint32_t ODR;
    0 R! v. n% A1 E5 [2 u
  20.   __IO uint32_t BSRR;
    3 q) F. x7 `7 I# S
  21.   __IO uint32_t BRR;( O- L7 ~1 [+ z- \
  22.   __IO uint32_t LCKR;3 X+ X9 L/ c1 F' R# }( M) C5 T: J
  23. } GPIO_TypeDef;
    5 g- J6 d3 u+ C/ ?% e3 x

  24. 5 v4 i7 u5 b( y8 Z( H
  25. /*stm32f10x.h 1315-1321;*/7 o/ r, M9 F9 ]" y8 i& d
  26. #define GPIOA_BASE            (APB2PERIPH_BASE + 0x0800)
    . t% _, I; h, a; D7 U0 I* J" p2 X" ^. N
  27. #define GPIOB_BASE            (APB2PERIPH_BASE + 0x0C00)0 u3 O) p' c3 ^, H" R5 d2 ?
  28. #define GPIOC_BASE            (APB2PERIPH_BASE + 0x1000)7 w4 Z. }4 f7 G& [/ M9 G% {0 j
  29. #define GPIOD_BASE            (APB2PERIPH_BASE + 0x1400)
    - ^1 N. Z+ }& X" x, p! a5 R5 [
  30. #define GPIOE_BASE            (APB2PERIPH_BASE + 0x1800)4 M: `- A' o# a! p% }  `$ K2 M8 G1 P
  31. #define GPIOF_BASE            (APB2PERIPH_BASE + 0x1C00)
    / _- V+ X5 W2 V5 i4 _& v
  32. #define GPIOG_BASE            (APB2PERIPH_BASE + 0x2000)
    4 M$ |, m9 d0 G) B! e0 M
  33. : \4 T& C7 B* e  z: W
  34. /*stm32f10x.h 1282-1283; GPIO都属于APB2总线,使用的时候要使能APB2总线的时钟源;*/
    4 U" W5 D# K. s' ^5 X) n  r: ~! A
  35. #define APB1PERIPH_BASE       PERIPH_BASE
    ! `- q! F: r/ R1 w: B8 ~
  36. #define APB2PERIPH_BASE       (PERIPH_BASE + 0x10000)( O1 U: d3 V2 M2 W! k, M/ `' l5 J
  37. / v, c- d0 `/ N  T& p: F  i: ?" s
  38. /*stm32f10x.h 1274*/
    ; Z( l3 C2 l' C& G
  39. #define PERIPH_BASE           ((uint32_t)0x40000000)
复制代码
4 u4 f, W5 d$ R; c0 ^
2 GPIO 寄存器
  GPIO涉及的寄存器较多,复用功能和重映射功能都需要配置专门的AFIO寄存器,但是本节暂时没有涉及;
  本节主要介绍了通用GPIO口涉及到的7个寄存器,具体寄存器的说明和使用如下;
  2.1 CRL端口配置低寄存器,CRH端口配置高寄存器 control register low,control register high;
    CRL和CRH都是32位寄存器,如下图所示,一起用来控制该组GPIO port的16个引脚配置;

2 l0 a7 S2 [; x& K) K
1371863-20200515102909983-458893530.png
   
    2.1.1 CRL和CRH寄存器复位值为0x4444_4444;CRL偏移地址:0x00,CRH偏移地址:0x04;配置信息封装如下;
  1. //定义了CRH和CRL寄存器需要的参数;以下声明在stm32f10x_gpio.h的前200行;
    1 _0 V9 c; v; a7 _6 q6 {+ U$ c
  2. typedef struct' _# x$ U' h; m, L
  3. {$ c8 M' v- y& r  Y
  4.   uint16_t GPIO_Pin;                /*用16位bit的每一位分别表示一个引脚*/            
    4 z# n0 I& Q8 G1 ~7 M8 J
  5.   GPIOSpeed_TypeDef GPIO_Speed;     /*用2位bit来表示输出模式的最大速度*/
    4 A# x) M, |' ^. V; ~0 x, F
  6.   GPIOMode_TypeDef GPIO_Mode;       /*CNF MODE,具体见结构体*/
    2 v  b& `, ~$ b: A1 w* ?
  7. }GPIO_InitTypeDef;
    ! ?5 B. L% b5 v- z6 Y# f& y  k
  8. 1 ~  R1 ^6 N" x% I% d" O
  9. #define GPIO_Pin_0                 ((uint16_t)0x0001)  /*0000 0000 0000 0001b*/$ y1 K. v, B/ I! ~8 e8 H! p0 W0 v
  10. #define GPIO_Pin_1                 ((uint16_t)0x0002)  /*0000 0000 0000 0010b*/
    " R! ^+ v/ ^3 i$ p0 y* `0 |  D" W- [
  11. #define GPIO_Pin_2                 ((uint16_t)0x0004)  /*0000 0000 0000 0100b*/
    4 l; q" m* k4 m& ]' s
  12. #define GPIO_Pin_3                 ((uint16_t)0x0008)  /*0000 0000 0000 1000b*/
    , O4 ?  K6 U/ a
  13. #define GPIO_Pin_4                 ((uint16_t)0x0010)  /*0000 0000 0001 0000b*/( C3 }# T# h6 i
  14. #define GPIO_Pin_5                 ((uint16_t)0x0020)  /*0000 0000 0010 0000b*/
    9 ?% ?! I$ G8 a, C4 h) g2 q9 J
  15. #define GPIO_Pin_6                 ((uint16_t)0x0040)  /*0000 0000 0100 0000b*/3 j8 D. m3 b5 @$ }
  16. #define GPIO_Pin_7                 ((uint16_t)0x0080)  /*0000 0000 1000 0000b*/+ e/ i/ V/ U% ?( j8 l0 o
  17. #define GPIO_Pin_8                 ((uint16_t)0x0100)  /*0000 0001 0000 0000b*/8 W3 D) t& W3 ^) T; p% w
  18. #define GPIO_Pin_9                 ((uint16_t)0x0200)  /*!< Pin 9 selected */
    5 d: t6 F* L+ @8 v/ _
  19. #define GPIO_Pin_10                ((uint16_t)0x0400)  /*!< Pin 10 selected *// C2 J. P) v  U$ M
  20. #define GPIO_Pin_11                ((uint16_t)0x0800)  /*!< Pin 11 selected */( S7 d9 |' B) O( A3 D3 ^7 W; ~
  21. #define GPIO_Pin_12                ((uint16_t)0x1000)  /*!< Pin 12 selected */
    8 M) x) q8 [' V1 \
  22. #define GPIO_Pin_13                ((uint16_t)0x2000)  /*!< Pin 13 selected */, C8 a1 }4 c! P0 p
  23. #define GPIO_Pin_14                ((uint16_t)0x4000)  /*!< Pin 14 selected */; _' i- I6 c1 q8 T# c3 v0 [5 q
  24. #define GPIO_Pin_15                ((uint16_t)0x8000)  /*!< Pin 15 selected */
    + @% n  S6 X; I) A" i5 v
  25. #define GPIO_Pin_All               ((uint16_t)0xFFFF)  /*!< All pins selected */
    2 b: ?% B& t% E4 w
  26. #define IS_GPIO_PIN(PIN) ((((PIN) & (uint16_t)0x00) == 0x00) && ((PIN) != (uint16_t)0x00))- I& W* P3 m. {7 |* i* t" d7 K
  27. * y, }- j% _: J4 |3 [1 Z
  28. typedef enum: f, D; B% C) U
  29. {
    : T: h7 O& M& B, H" b
  30.   GPIO_Speed_10MHz = 1,     /*output MODE[1:0]*/
    / }, A$ ]0 ^6 p5 ~# \- j
  31.   GPIO_Speed_2MHz,          /*output MODE[1:0]*/
    4 T& R: l2 P0 o. p# O
  32.   GPIO_Speed_50MHz          /*output MODE[1:0]*/# @  w. t4 \" d  q7 w4 C# [
  33. }GPIOSpeed_TypeDef;0 J2 {4 [8 Q  b. W/ R8 N6 J, g
  34. #define IS_GPIO_SPEED(SPEED) (((SPEED) == GPIO_Speed_10MHz) || ((SPEED) == GPIO_Speed_2MHz) || \
    & p' m  W9 y5 |$ d% v
  35.                               ((SPEED) == GPIO_Speed_50MHz))8 f: }7 l& v. M
  36.                               
    / c6 e3 z/ i2 n  a. u) j# C
  37. typedef enum- J0 ]+ U" i2 R$ |8 t8 _
  38. { GPIO_Mode_AIN = 0x0,                /*0000 0000b [4]0 input [3:0]CNF+MODE*/
    * s, J* B$ P9 |' [/ s, |
  39.   GPIO_Mode_IN_FLOATING = 0x04,       /*0000 0100b [4]0 input [3:0]CNF+MODE*/; m  ?: Y% ~2 D5 I0 _
  40.   GPIO_Mode_IPD = 0x28,               /*0010 1000b [4]0 input [5]下拉,[3:0]CNF+MODE*/7 G" J8 t) N" o
  41.   GPIO_Mode_IPU = 0x48,               /*0100 1000b [4]0 input [6]上拉,[3:0]CNF+MODE*/
    ) i$ T. x0 U7 r! n  i0 H+ i. m
  42.   GPIO_Mode_Out_OD = 0x14,            /*0001 0100b [4]1 output,[3:2]CNF*/
    ; O* o  Q" F# y* n  B) o7 R% u: g
  43.   GPIO_Mode_Out_PP = 0x10,            /*0001 0000b [4]1 output,[3:2]CNF*/) R  c) E6 N$ g1 d2 E1 C( d5 `
  44.   GPIO_Mode_AF_OD = 0x1C,             /*0001 1100b [4]1 output,[3:2]CNF*/
    1 F! j5 X+ U4 w
  45.   GPIO_Mode_AF_PP = 0x18              /*0001 1000b [4]1 output,[3:2]CNF*// P& P. p2 T4 W+ ?( u+ {* [
  46. }GPIOMode_TypeDef;
    0 q* _8 ?, z* c9 R& Z- N
  47. #define IS_GPIO_MODE(MODE) (((MODE) == GPIO_Mode_AIN) || ((MODE) == GPIO_Mode_IN_FLOATING) || \
    ! u! }$ x& b1 I' x5 F$ U( I
  48.                             ((MODE) == GPIO_Mode_IPD) || ((MODE) == GPIO_Mode_IPU) || \
    ! I) P! C" T; ?5 }/ H2 D
  49.                             ((MODE) == GPIO_Mode_Out_OD) || ((MODE) == GPIO_Mode_Out_PP) || \
    - I) z2 p) U+ i' X: Z
  50.                             ((MODE) == GPIO_Mode_AF_OD) || ((MODE) == GPIO_Mode_AF_PP))( P* j& }& g  u1 x7 s4 M
  51. #define IS_GET_GPIO_PIN(PIN) (((PIN) == GPIO_Pin_0) || ((PIN) == GPIO_Pin_1) ||((PIN) == GPIO_Pin_2) || \: G+ e6 q1 h* K/ q: G
  52.                               ((PIN) == GPIO_Pin_3) || ((PIN) == GPIO_Pin_4) || ((PIN) == GPIO_Pin_5) || \0 g* F4 x6 b" i9 a8 E. j9 h
  53.                               ((PIN) == GPIO_Pin_6) || ((PIN) == GPIO_Pin_7) || ((PIN) == GPIO_Pin_8) || \
    $ T& f) {8 Q0 ^& N- q3 j8 O
  54.                               ((PIN) == GPIO_Pin_9) || ((PIN) == GPIO_Pin_10) || ((PIN) == GPIO_Pin_11) || \
    4 y4 |, S# ^$ P; l
  55.                               ((PIN) == GPIO_Pin_12) ||((PIN) == GPIO_Pin_13) || ((PIN) == GPIO_Pin_14) || \- K( S( W% x, V" {7 P
  56.                               ((PIN) == GPIO_Pin_15))' [+ `# ^3 h( ~+ m

  57. 1 x: }) h3 Q5 P: Q* h- x) R
  58. typedef enum( @0 T7 d9 r3 I
  59. { Bit_RESET = 0,
    7 e8 w2 B3 ]8 L
  60.   Bit_SET
    2 Z: @  m7 v/ g: y$ H# B
  61. }BitAction;
复制代码
% J* u* o) W& j' C/ y1 Z
    2.1.2 配置CRL和CRH的初始化代码如下;
  1. /*以下代码位于stm32f10x_gpio.c中,配置相应port的CRL和CRH*/5 Y/ c+ C: B0 v# h8 d
  2. void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
    % t) F$ O. F7 X# ]' I
  3. {$ s$ E4 g! g( k+ z7 |
  4.   uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;7 Y1 f' F$ x( y9 T( g) W; Q
  5.   uint32_t tmpreg = 0x00, pinmask = 0x00;
    . \8 y3 L! J! D) L' o1 X
  6.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));/ k; U' r0 r' o) @  _
  7.   assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));/ J+ q" |. O$ O( R! s
  8.   assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin));  
    ! m3 C  E) b, x; U. ?% L: H! R9 A
  9.     }) z9 d1 s; I
  10.   currentmode = ((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x0F); //currentmode保留了GPIO_Mode[3:0];
    * j7 i8 A. O; [  I% H  O

  11. ' c6 A1 ~/ ]0 W
  12.   if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)//如果GPIO_Mode[4]为1,表示为输出模式;1 T: _$ P. K+ q0 e7 K; N
  13.   { $ `1 k! l& m1 l7 ^
  14.      /*if(输出模式),将CNF[1:0]和MODE[1:0]的信息保存到currentmode[3:0]*/8 f( W2 A5 N$ \( |
  15.     assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));1 Y5 w" t! `$ H' x" ~! ?7 B8 \
  16.     currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;//currentmode或上GPIO_Speed[1:0];
    * Z2 F, b3 q7 d8 ?5 N' J' F+ B
  17.   }
    1 Q% C- W' R  E* k% z8 F4 H
  18.   $ e1 w! m# n; \) }5 C! ^$ b
  19.   /*以下部分为CRL Configuration*/
    2 g1 z+ v7 R: l8 j6 B
  20.   if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00)        //if(是低8位pin);' z# N' s1 m$ ~
  21.   {/ p0 ?) V2 |4 ]: v7 T3 |
  22.     tmpreg = GPIOx->CRL;                                //temreg存放CRL寄存器的信息;
    7 j- T0 I0 R* t/ F3 M
  23.     for (pinpos = 0x00; pinpos < 0x08; pinpos++)        //pinpos为几,表示引脚几;        # m) q" k6 k" W0 }+ W7 a
  24.     {
    . R1 t* M  @) ^3 |7 Q9 U7 m
  25.       pos = ((uint32_t)0x01) << pinpos;                    # S$ ^' X/ \# B6 I+ G- t7 H
  26.       currentpin = (GPIO_InitStruct->GPIO_Pin) & pos;    //currentpin:要么为当前pin值,要么为0;* {) r! P1 A* @
  27.       if (currentpin == pos)) {( n) u# ^8 e+ \1 q8 x
  28.       {
    " d/ {: Z8 W  ^9 o6 S
  29.         pos = pinpos << 2;                                //pos:引脚对应的CRL配置位; C- _9 r1 @" K( g
  30.         pinmask = ((uint32_t)0x0F) << pos;                //pinmask:引脚对应的CRL[3:0]置1' u( R( f- g. Q7 T$ Y9 P
  31.         tmpreg &= ~pinmask;                                //temreg中对应引脚的[3:0]清0
    / {8 i7 P6 ?  b
  32.         tmpreg |= (currentmode << pos);                    //temreg中对应引脚的[3:0]配置成currentmode[3:0], e/ B: z8 G1 A# I/ r) ^/ \! g
  33. ! G& L: c2 T+ q, R
  34.         //此处的if else应该是通过ODR来配置硬件,由中文参考手册8.1.7原理图推测可知       & m5 m5 d, ^. u! Y
  35.         if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD) //if(输入连下拉电阻)
    ( j5 \) [8 Z5 p+ X3 @8 x
  36.         {
    # `, [5 q9 w& r( W+ G9 k$ Z4 P
  37.           GPIOx->BRR = (((uint32_t)0x01) << pinpos);     //通过配置ODR的16bit,对应pin的bit置0,连接下拉电阻;% c! k; B" \/ [0 \1 F* w# ^
  38.         }
    7 r9 L4 h7 j$ M) J
  39.         else
    $ v* \, X5 s/ _: k( T
  40.         {7 B, I6 V2 B$ r( J8 J
  41.           if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)//if(输入连上拉电阻)/ u4 x+ a$ y1 n: x; ^( s
  42.           {! R+ N% f% {% [) {
  43.             GPIOx->BSRR = (((uint32_t)0x01) << pinpos);     //通过配置ODR的16bit,对应pin的bit置1,连接上拉电阻;2 O$ S+ a/ s0 L' a6 O  y
  44.           }- S6 |# i5 p% w8 h
  45.         }
    . d* |) a4 }& c& Q
  46.       }
    / t; N$ L  p- C8 v: ]# ~+ A
  47.     }) r) v% }) K. x! ]% J6 Q$ s2 `
  48.     GPIOx->CRL = tmpreg;  //把配置好对应pin脚的temreg放回CRL中
    5 [2 Y8 {0 R# P! h. o2 r: c! B2 s
  49.   }9 f/ _) p( s: Z
  50.   
      H4 ?8 g$ Q5 k% V* U1 K
  51. /*---------------------------- GPIO CRH Configuration ------------------------*/
    * c% p& |* Z; F- x
  52.   /* Configure the eight high port pins */% {: n7 S2 x8 F* q3 B5 t( u
  53.   if (GPIO_InitStruct->GPIO_Pin > 0x00FF)3 G; \* ?. m# T0 u6 G
  54.   {
    6 ^# E6 Q( n: J# l" }- E0 H; h
  55.     tmpreg = GPIOx->CRH;
    5 _8 |9 p/ N, p5 I8 v
  56.     for (pinpos = 0x00; pinpos < 0x08; pinpos++)
    ; \0 A  _) r+ ?- ?
  57.     {
    * C) I2 m: \* r% o; O( `
  58.       pos = (((uint32_t)0x01) << (pinpos + 0x08));
    - N* H9 Q: Z7 O/ x& R9 h- j
  59.       /* Get the port pins position */8 \% U6 X1 ?. u1 r$ ^7 L. I4 B" y
  60.       currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);
    7 d/ ?/ {' p% g* T# f6 S5 N+ W
  61.       if (currentpin == pos)
    5 a+ _! X5 ~) r# ?9 w
  62.       {
    4 K7 d& L0 [5 K  g* L; m$ B
  63.         pos = pinpos << 2;
    6 t  H) V* G1 m% w
  64.         /* Clear the corresponding high control register bits */
    0 K% l2 c% N7 X7 H* |3 `1 j: R
  65.         pinmask = ((uint32_t)0x0F) << pos;
    * m: A+ L! E/ b8 z1 m5 r2 d
  66.         tmpreg &= ~pinmask;
    - E$ y6 s9 M# k) J; q
  67.         /* Write the mode configuration in the corresponding bits */2 ?8 Q$ r% T- D  g: y( H
  68.         tmpreg |= (currentmode << pos);1 ]$ m( Q7 a! e( O6 j& z( M7 t
  69.         /* Reset the corresponding ODR bit */* z# H; U& |! u3 P
  70.         if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)1 @) ^6 t. \* l# Z+ e! ~* n
  71.         {& I; s; u- x8 d. {/ i, a
  72.           GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));2 F  H- T  V' w  {1 t- B/ Y) y6 I
  73.         }! L7 G: G4 X' p) t) y
  74.         /* Set the corresponding ODR bit */5 G, V% N6 m. Q5 |( u% Y
  75.         if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
    ! q& b" q' [4 b- H& p8 H: J
  76.         {7 p& s) k& `. X: x" E! g0 O
  77.           GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));2 |$ F' \% Z9 s: E9 w8 w
  78.         }! |# d! T9 K8 c7 [4 N3 L2 [) P
  79.       }
    $ \- d; |  C7 s/ |
  80.     }
    2 t: e3 j3 K' u- d2 v, v
  81.     GPIOx->CRH = tmpreg;
    . V- d4 e) |8 g+ m6 m3 o& r' T8 H
  82.   }* [* N- ~7 E$ W, o, [: ?
  83. }
复制代码

5 {6 T' o! A0 e' A3 L- e: P5 [, P
    2.1.3 推挽输出,开漏输出,上拉输入和下拉输入的原理推荐参考安富莱文档的解释
  2.2 LCKR端口配置锁定寄存器:lock register
    复位值为0x0000_0000;偏移地址:0x18;
    用来锁存对应端口的CRL,CRH寄存器的配置;修改完LCK[15:0]然后锁定[LCKK],对应的CRL,CRH配置将会持续到下次系统复位信号来临;
6 l2 W0 q2 A8 l) A- Z# u3 K
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即可;
9 E1 O4 Z5 n; H
1371863-20200515110114147-1808242956.png
 
    2.3.1 IDR寄存器的使用函数
  1. /*以下代码位于stm32f10x_gpio.c中*1 读取IDR寄存器某一位的值,即pin值;*2 读取IDR寄存器的值,即port值;*/
    * _1 ^; [* t; b( q& J, @+ H
  2. uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)2 m+ Q( {# a* _$ a6 O
  3. {0 I2 r7 A# g/ o1 P
  4.   uint8_t bitstatus = 0x00;
    0 x! h1 T- y2 t' b: }  q8 a* R0 s
  5.   2 K0 s) D6 g) g( R% H
  6.   /* Check the parameters */% g' a7 M3 f* g. S5 U* C
  7.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));+ ^) k3 E- D  _
  8.   assert_param(IS_GET_GPIO_PIN(GPIO_Pin)); , [2 C# F7 \, z

  9. 8 k8 X4 N: W7 Z3 K
  10.   /*先读取整个IDR寄存器,然后通过&来读取bit,IDR寄存器的最小处理单位是16bit*/4 J6 R' L, f) p8 m/ U  s+ a" W8 F
  11.   if ((GPIOx->IDR & GPIO_Pin) != (uint32_t)Bit_RESET)$ U& S3 a$ A2 y2 O: N
  12.   {
      Z% Y! \& C% c; P5 t1 N
  13.     bitstatus = (uint8_t)Bit_SET;
    1 K: Z5 g& J9 c% p4 w
  14.   }
    ' Y$ w5 N$ ]* y0 i# m! }
  15.   else* x% U$ W1 ~" K! ]0 `2 Q+ f1 U
  16.   {
    ; S. u% N/ _0 j
  17.     bitstatus = (uint8_t)Bit_RESET;
    / K: s# w9 C0 ^/ n* E5 \- @
  18.   }* w3 v- g# z* i- r4 `. i! x
  19.   return bitstatus;
    - d8 r" N* r6 \! }
  20. }
    ( X' `, [* O- R0 {, T

  21. % E& p4 f! N3 ~7 T" r0 N
  22. uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)1 ^6 E% e& {/ ~6 c& U1 W
  23. {
    ( O. n/ }6 o2 |: j
  24.   /* Check the parameters */
    1 J" o) S  {; N; @- Z
  25.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
    1 W) Y% y6 n# t5 e; [
  26.   
    $ R8 A& S% l/ O3 G) V. f$ g0 {4 b
  27.   return ((uint16_t)GPIOx->IDR);
    1 b: T; @/ J0 }. t/ |
  28. }
复制代码
    2.3.2 ODR寄存器的使用函数
  1. /*以下代码位于stm32f10x_gpio.c中;. ~+ N4 m' J6 o% t) j! `5 y
  2. *1 读取ODR寄存器某一位的值;即读取pin值;
    7 V! y. @% F/ T' s* l5 _
  3. *2 读取ODR寄存器的值;即读取port值;$ B8 {, s7 C! T  l( o
  4. *3 向ODR寄存器写入port值;*/
    , \! l1 z  }# \0 ^9 S. R8 U" B
  5. uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)1 U1 Y6 z, y( Z2 B; ]
  6. {1 N: P2 W6 s& h' f. H8 \: B
  7.   uint8_t bitstatus = 0x00;
    0 b, y( E" ^- b1 T  O5 K
  8.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
      U; e  U' d- \& U( Q
  9.   assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
    ( x( e3 v0 Q/ i

  10. 3 w  ], m: n8 S, g0 P
  11.    /*先读取整个ODR寄存器,然后通过&来读取bit,ODR寄存器的最小处理单位是16bit*/
    ) T! `6 [9 H, V
  12.   if ((GPIOx->ODR & GPIO_Pin) != (uint32_t)Bit_RESET){
    % z/ E3 F* i$ g1 M
  13.     bitstatus = (uint8_t)Bit_SET;
    # ?3 V) W9 }9 y
  14.   }3 P: K1 Z/ H( y0 ^. d7 {
  15.   else{
    0 ]" h* p0 ~8 q
  16.     bitstatus = (uint8_t)Bit_RESET;" X, r) O1 d: I
  17.   }* c& p- Q+ ]2 D
  18.   return bitstatus;( G' ]! r/ ^) P1 }
  19. }. K- G2 p- C$ G: S) P, \

  20. ( f9 k4 W, ^$ u& V' I, r! N1 o# j! P
  21. uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)  v" e: l( A/ _1 d% r
  22. {
    , A9 N( a+ S7 T' J
  23.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));   / Z$ _9 b% s* U7 y8 y: y2 A8 [
  24.   return ((uint16_t)GPIOx->ODR);, E) k7 u$ E  u' t1 [
  25. }, H$ L# ~/ p/ a: S. Y" J/ V
  26. * E1 W/ C: f8 D& K4 u: I4 X: W- L
  27. void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)
    7 e) A& y3 d5 O6 {5 w
  28. {
    + j# H/ L- p/ N6 ?; R3 G' a
  29.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
    / j2 \( T0 i* M2 i
  30.   GPIOx->ODR = PortVal;
    8 r2 l: T0 ]( X1 B; t
  31. }
复制代码
  2.4 BSRR端口置位/复位寄存器,BRR端口复位寄存器:bit set/reset register,bit reset register;
    复位值为0x0000_0000;BSRR偏移地址:0x10;BRR偏移地址:0x14;
    对BSRR,BRR的操作,就是对该组GPIO口的ODR寄存器寄存器的操作;
; g& r& ^- `: _4 t" X0 z
1371863-20200515113753656-753752303.png
    
    2.4.1 BSRR寄存器:pin置1
  1. void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin), |5 K! @5 F+ R" B5 f9 N4 e
  2. {
    $ A7 |0 k# T8 V5 F9 J# o' L% g3 r
  3.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
    + E) i# I, }3 Z$ v+ |& u
  4.   assert_param(IS_GPIO_PIN(GPIO_Pin));
    ' c0 C! V; L7 u( K/ r& p/ G3 i
  5.   , s  }: O( a0 M% p* u$ D" t
  6.   GPIOx->BSRR = GPIO_Pin;, y- A& k1 }* y- {
  7. }
复制代码
1 r) O, n& A$ @
    2.4.2 BRR寄存器:pin清0
  1. void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)# C$ U: E, \" p% a3 H
  2. {- L% N) F0 }+ J9 ^8 a* {% a- ]
  3.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
    % k2 \3 ]# n$ M: s
  4.   assert_param(IS_GPIO_PIN(GPIO_Pin));; K2 r- E4 X3 H9 G7 `
  5.   ) e, @+ U% b. e& F3 S
  6.   GPIOx->BRR = GPIO_Pin;' \; _# ]$ i0 `% {
  7. }
复制代码

9 P$ x0 c* H0 f, q; X) t' o) |
    2.4.3 设置pin脚相应的数值:pin的置1清0
  1. void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)4 \* Z2 z$ m2 n/ e
  2. {
    6 H. ^( d2 b" @2 m6 q2 W+ D! h
  3.   assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
    ; H% m" d9 g# p; x; m' T- B
  4.   assert_param(IS_GET_GPIO_PIN(GPIO_Pin));/ C( U5 N2 D0 J* b2 d: Q, ~8 j
  5.   assert_param(IS_GPIO_BIT_ACTION(BitVal)); " d# E1 v7 z8 E0 f/ U# p
  6.   
    4 N4 z3 c; y# j+ G
  7.   if (BitVal != Bit_RESET)
    . H( R  ^8 D0 ~) P
  8.   {- o+ c& |- c3 K9 c7 o1 s' I
  9.     GPIOx->BSRR = GPIO_Pin;, K0 B9 H: y( b) X
  10.   }4 F9 |% O( I* p% K, |8 ?* O
  11.   else
    + w& i7 _: [( o% k
  12.   {; R- y4 k. k) B6 [5 O
  13.     GPIOx->BRR = GPIO_Pin;
    . z* i2 P/ L6 U! r/ o( u
  14.   }4 T: K( J! ~1 M- y1 t
  15. }
复制代码
% }  _) L( x2 Q! \  g' [
  虽然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"
    & ]5 W* Q$ e: _) S1 j

  2.   t3 ?9 ^; a( W! I+ M% P) b% `
  3. int main(void)
    6 X# m- q: H6 W% T2 h
  4. {
    " p" y8 J/ S- f" M. W/ G# c
  5.     RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    8 [* \: Y* f' A# _: W
  6.     GPIO_InitTypeDef GPIO_Struct;
    ) m' b% x' z; a  f9 F6 A

  7. , g7 `' B/ t/ B2 S
  8.     //portA pin1 as input;     
    ' B+ g4 r4 e: f5 Y. N
  9.     GPIO_Struct.GPIO_Pin = GPIO_Pin_1;             0 X* N7 @8 o; g! W# q
  10.     GPIO_Struct.GPIO_Mode =GPIO_Mode_IPD ;     
    ! }9 h0 [/ }' J6 w1 `' r
  11.     GPIO_Init(GPIOA, &GPIO_Struct);
    4 \+ m( a# c' R1 d/ C: t1 c
  12.     $ E5 {2 P5 z+ e; q: b
  13.     //portA pin2 as output;                        
    2 v# U4 I  D: u
  14.     GPIO_Struct.GPIO_Pin = GPIO_Pin_2;
    $ v/ i: @, U8 |/ U1 S
  15.     GPIO_Struct.GPIO_Speed = GPIO_Speed_50MHz;+ R# [( t; W6 I( ~+ `
  16.     GPIO_Struct.GPIO_Mode = GPIO_Mode_Out_PP;9 |6 c' E4 X- i. {5 o, N+ `' t
  17.     GPIO_Init(GPIOA, &GPIO_Struct);                                          
    " V( Y9 i& U; f" o1 r3 T
  18.     while(1)+ y2 q$ m, e; X4 j* P
  19.     {  z# v, p4 g8 H0 u" W: E
  20.         GPIO_SetBits(GPIOA,GPIO_Pin_2);
    ; V# d" I% A, {/ z3 a
  21.         delay_ms(100);1 P* X  q3 s$ u% |
  22.         GPIO_ResetBits(GPIOA,GPIO_Pin_2);
    6 H, t* _$ \. H, w4 I% T4 {
  23.         delay_ms(100);
    & _  {7 e$ P! `" U8 Z3 y: a
  24.     }
    + c. m% y+ ?, b" z- }
  25. }
复制代码

7 l8 m9 k0 Q! S# d
5 小结
  本文主要是结合通用GPIO口的寄存器,对标准库中通用GPIO口的代码进行了分析和概括;
  然后提供了一个代码示例;如果只是使用而不想理解标准库原理,则直接使用接口函数即可,如代码示例所示;

8 v, h  p; ~5 i/ k6 ]( _# z# ^
赞 收藏 评论0 发布时间:2022-2-6 19:49

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版