前言 "GPIO的使用1"中主要从内核代码开始,从寄存器的地址映射开始,对GPIO的封装和操作执行逻辑详细分析了一下; 内核的函数接口标准是都是一样的CMSIS,了解了GPIO外设的原理,也就了解了其他外设是如何封装的; GPIO使用时先确定是否为外设复用;目的是确定输入输出数据是给外设处理,还是存放在GPIO寄存器里就完了; 然后确定IO的输入输出模式;目的是通过软件配置,选择端口在芯片内部的电路连接方式; GPIO上电默认为浮空输入模式,禁止了上下拉电阻;上下拉电阻默认30-50kΩ;保护二极管防止电流反向击穿; 6 Q! k' Q2 {+ P- D* ?
+ }) [' 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;具体硬件参数见数据手册; 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映射到地址上的; / p" l/ K8 d6 p
- /*stm32f10x.h 1408-1414行声明如下*/2 u r( B/ h/ U& v% l* c, P
- #define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)
' @4 ^3 X. t( B r+ P - #define GPIOB ((GPIO_TypeDef *) GPIOB_BASE)3 A; b# O! I/ L
- #define GPIOC ((GPIO_TypeDef *) GPIOC_BASE); O, |5 Y# k! ~1 W
- #define GPIOD ((GPIO_TypeDef *) GPIOD_BASE); L7 h6 u0 g# l! [
- #define GPIOE ((GPIO_TypeDef *) GPIOE_BASE)6 c! }' U- \: U- ?3 D8 v! D
- #define GPIOF ((GPIO_TypeDef *) GPIOF_BASE)
; Z. k- w- M* ?* [ {2 T& q - #define GPIOG ((GPIO_TypeDef *) GPIOG_BASE)
2 i6 b* m+ X8 F. T6 F! d3 `
2 f6 {' k' l) X- /* stm32f10x.h 1001-1010行;0 W2 I# }2 i4 A; q* Z- P
- *把结构体的首地址映射到GPIO的首寄存器地址,就可以通过该结构体对硬件寄存器操作;
; I6 H! V# l4 q6 L2 S5 }+ } - *结构体的地址通过结构体指针来赋值对应上*/
% I% S) F7 U+ h( {6 s" S - #define __IO volatile /*core_cm3.h NO.116*/: g" `+ F5 Y8 k) X
- typedef struct
2 M5 A1 Y# a% g- s0 I) P - {
# z1 R3 ^( C0 x - __IO uint32_t CRL;# E" }( D4 D1 V+ ?+ z
- __IO uint32_t CRH;
) U. `8 |- b* c9 \ - __IO uint32_t IDR;9 t" t5 v/ R/ G# t, n% n
- __IO uint32_t ODR;
9 }6 z- a! e& R/ z9 n - __IO uint32_t BSRR;
7 V( L4 w; T" r& U - __IO uint32_t BRR;' W% l5 J, ^& b' F# x0 @
- __IO uint32_t LCKR;
! G; s8 ]' |9 u- ^! @. c - } GPIO_TypeDef;
* G# H; I: _8 U- P
0 X# f7 M; T4 \- /*stm32f10x.h 1315-1321;*/
; M, c5 G. R) _ - #define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)2 A; ?$ ^4 G' X8 C9 {; G2 ?( |1 p: j
- #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)
! S, @3 [5 z+ W8 ?9 I& G% P - #define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)
7 e; n( o" A3 m+ Z# ~4 k0 @9 u - #define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)3 m0 A! _+ v$ B9 v
- #define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)& r7 F4 n5 n+ ]; D
- #define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)
8 H* l w7 h7 M. ]5 \9 z* G - #define GPIOG_BASE (APB2PERIPH_BASE + 0x2000) ~- L- M- {! |: ]# d7 {
; L: K' |4 O1 `0 M/ e, Q3 v- /*stm32f10x.h 1282-1283; GPIO都属于APB2总线,使用的时候要使能APB2总线的时钟源;*/
5 h1 y1 B+ f1 L+ F3 g# Q - #define APB1PERIPH_BASE PERIPH_BASE' S2 M5 w! r$ x b
- #define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)
% ^1 V/ n# }1 M4 M2 ?# W/ Y) ]: G2 ` - $ o% _/ e8 b9 v' g
- /*stm32f10x.h 1274*/$ a0 T0 W0 O: E6 i
- #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
2.1.1 CRL和CRH寄存器复位值为0x4444_4444;CRL偏移地址:0x00,CRH偏移地址:0x04;配置信息封装如下; - //定义了CRH和CRL寄存器需要的参数;以下声明在stm32f10x_gpio.h的前200行;
* v- W4 ~) d9 y+ y! i - typedef struct: i* G" t6 q1 Z+ P/ n
- {
* @ {+ w9 P. w( L( u/ t& k( U - uint16_t GPIO_Pin; /*用16位bit的每一位分别表示一个引脚*/ ) ?9 I: h; ~" _0 f+ _2 B7 h
- GPIOSpeed_TypeDef GPIO_Speed; /*用2位bit来表示输出模式的最大速度*/
1 W; q! h, F0 ^; J3 Y1 }: b - GPIOMode_TypeDef GPIO_Mode; /*CNF MODE,具体见结构体*/
8 i% z' B% v I6 Y) ~) _ - }GPIO_InitTypeDef;
% E$ b8 ]! `0 l5 n5 @7 T/ N - . d' a9 h$ z. B' r
- #define GPIO_Pin_0 ((uint16_t)0x0001) /*0000 0000 0000 0001b*/
! Z& @- H1 p9 l; g - #define GPIO_Pin_1 ((uint16_t)0x0002) /*0000 0000 0000 0010b*/+ \" C% z1 [2 N; x" b0 u
- #define GPIO_Pin_2 ((uint16_t)0x0004) /*0000 0000 0000 0100b*/
9 [; C$ f+ F$ ]' R# Q7 u6 C8 Q - #define GPIO_Pin_3 ((uint16_t)0x0008) /*0000 0000 0000 1000b*/
2 O! Y$ ^5 c& a; K& J6 j/ [; W - #define GPIO_Pin_4 ((uint16_t)0x0010) /*0000 0000 0001 0000b*/
# E; Q! _; X4 @4 L" t) d4 x - #define GPIO_Pin_5 ((uint16_t)0x0020) /*0000 0000 0010 0000b*/& L( e* D' G: r- q
- #define GPIO_Pin_6 ((uint16_t)0x0040) /*0000 0000 0100 0000b*/9 |% R5 {& d, ~! L
- #define GPIO_Pin_7 ((uint16_t)0x0080) /*0000 0000 1000 0000b*/
# P( x$ \3 a, _0 |9 O- }' I8 t# \ - #define GPIO_Pin_8 ((uint16_t)0x0100) /*0000 0001 0000 0000b*/
; q& A& @ V$ s+ t; d: c - #define GPIO_Pin_9 ((uint16_t)0x0200) /*!< Pin 9 selected */5 R" n, y% L) i) x- N" m
- #define GPIO_Pin_10 ((uint16_t)0x0400) /*!< Pin 10 selected */
. Z7 W9 m0 B r6 U2 O - #define GPIO_Pin_11 ((uint16_t)0x0800) /*!< Pin 11 selected */
/ [' h# _; V& p0 W - #define GPIO_Pin_12 ((uint16_t)0x1000) /*!< Pin 12 selected */
3 Q2 ^0 X( {. S$ l, J3 y5 F. z$ b - #define GPIO_Pin_13 ((uint16_t)0x2000) /*!< Pin 13 selected */% A7 J! l* z/ x+ m* @2 @( L
- #define GPIO_Pin_14 ((uint16_t)0x4000) /*!< Pin 14 selected */
* T6 N/ H, r7 \8 g; p B" W) } - #define GPIO_Pin_15 ((uint16_t)0x8000) /*!< Pin 15 selected */2 ?/ Q4 X7 d+ {
- #define GPIO_Pin_All ((uint16_t)0xFFFF) /*!< All pins selected */
# R' Q$ @1 Z( h" F - #define IS_GPIO_PIN(PIN) ((((PIN) & (uint16_t)0x00) == 0x00) && ((PIN) != (uint16_t)0x00))
8 @# K6 A2 Q7 W
1 p$ I/ b' Y! w& `1 Q- typedef enum
. S2 g# C$ p" K7 F: u - { 6 S! }+ V; Q( y8 k7 B
- GPIO_Speed_10MHz = 1, /*output MODE[1:0]*/" n% k( g2 D* R& ]. u. m
- GPIO_Speed_2MHz, /*output MODE[1:0]*/
# K- x. E2 F) x2 r/ E2 u - GPIO_Speed_50MHz /*output MODE[1:0]*/
$ j" L' i& R2 Z& R( c: K" n! l - }GPIOSpeed_TypeDef;4 H- L2 J# V. i, V" m: S# w4 d+ |2 o
- #define IS_GPIO_SPEED(SPEED) (((SPEED) == GPIO_Speed_10MHz) || ((SPEED) == GPIO_Speed_2MHz) || \
" P5 b, `: ^5 n6 h2 b2 m - ((SPEED) == GPIO_Speed_50MHz))2 d* l7 M+ ~( z: e. j
-
6 \ @5 x+ y- u: s9 y& z - typedef enum) f: z# j+ w+ G! c0 a1 |8 m
- { GPIO_Mode_AIN = 0x0, /*0000 0000b [4]0 input [3:0]CNF+MODE*/7 `" {7 j( r8 k4 i+ A
- GPIO_Mode_IN_FLOATING = 0x04, /*0000 0100b [4]0 input [3:0]CNF+MODE*/
9 L; [( G' R8 W4 u5 h5 q - GPIO_Mode_IPD = 0x28, /*0010 1000b [4]0 input [5]下拉,[3:0]CNF+MODE*/
9 \7 w% o) f+ y7 T0 Q - GPIO_Mode_IPU = 0x48, /*0100 1000b [4]0 input [6]上拉,[3:0]CNF+MODE*/6 ?! \& e3 q; ]/ Z+ W
- GPIO_Mode_Out_OD = 0x14, /*0001 0100b [4]1 output,[3:2]CNF*/
: `, y, v' R6 J! r3 K3 T - GPIO_Mode_Out_PP = 0x10, /*0001 0000b [4]1 output,[3:2]CNF*/
( \9 q+ x0 ?8 D" H0 @9 _+ ?! `8 }( \ - GPIO_Mode_AF_OD = 0x1C, /*0001 1100b [4]1 output,[3:2]CNF*/
/ H. A- H$ n! `6 i - GPIO_Mode_AF_PP = 0x18 /*0001 1000b [4]1 output,[3:2]CNF*/
) W( Z: n$ c2 v% W; ~ - }GPIOMode_TypeDef; % T3 B5 O, r7 t
- #define IS_GPIO_MODE(MODE) (((MODE) == GPIO_Mode_AIN) || ((MODE) == GPIO_Mode_IN_FLOATING) || \) B2 O6 @3 x3 h1 \" d
- ((MODE) == GPIO_Mode_IPD) || ((MODE) == GPIO_Mode_IPU) || \% B. d8 l; b& V' M
- ((MODE) == GPIO_Mode_Out_OD) || ((MODE) == GPIO_Mode_Out_PP) || \
2 }* P- ]4 O2 i6 c* s# r3 D8 ? - ((MODE) == GPIO_Mode_AF_OD) || ((MODE) == GPIO_Mode_AF_PP)), e g2 z g( d" j4 Q% i$ p! H( t! [
- #define IS_GET_GPIO_PIN(PIN) (((PIN) == GPIO_Pin_0) || ((PIN) == GPIO_Pin_1) ||((PIN) == GPIO_Pin_2) || \5 k+ J) ~& T" |
- ((PIN) == GPIO_Pin_3) || ((PIN) == GPIO_Pin_4) || ((PIN) == GPIO_Pin_5) || \
0 Y8 X% S& Z' r# n, W - ((PIN) == GPIO_Pin_6) || ((PIN) == GPIO_Pin_7) || ((PIN) == GPIO_Pin_8) || \3 L$ ^. l+ r! N/ y' |1 H0 ]
- ((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 - ((PIN) == GPIO_Pin_12) ||((PIN) == GPIO_Pin_13) || ((PIN) == GPIO_Pin_14) || \ V3 Q3 E8 O& f( o( ^. u
- ((PIN) == GPIO_Pin_15))
0 {9 l. t: y; @
- ?7 E# n1 j8 q1 K3 |. Z- Q- typedef enum- C$ x7 l) d" n3 \. W! {0 E; A- F; Y
- { Bit_RESET = 0,
8 O# {3 F# m, |4 b3 h - Bit_SET* A- x* ?1 ?( Y8 \2 b
- }BitAction;
复制代码
, I" C# _3 d; p 2.1.2 配置CRL和CRH的初始化代码如下; - /*以下代码位于stm32f10x_gpio.c中,配置相应port的CRL和CRH*/# f8 _3 A" F5 g7 Q* [! O8 w7 Z
- void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct) V' S6 o! q6 m3 Y2 ?
- {
- M, |) H0 ]. \0 Y, Z6 X - uint32_t currentmode = 0x00, currentpin = 0x00, pinpos = 0x00, pos = 0x00;# U5 ]5 j1 I) G5 O- K& Q
- uint32_t tmpreg = 0x00, pinmask = 0x00;
& m+ _4 y# l" ~# B8 }# v1 @3 P - assert_param(IS_GPIO_ALL_PERIPH(GPIOx));5 W Q4 Q! l) @$ |$ S
- assert_param(IS_GPIO_MODE(GPIO_InitStruct->GPIO_Mode));( P) W* n; S8 S' |' i9 O
- assert_param(IS_GPIO_PIN(GPIO_InitStruct->GPIO_Pin)); 8 h+ U# P( Y& O8 J" h) n
- , i1 h/ g7 T# t1 e \
- 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 - : f! n) @- Q1 d& G' S( }6 R7 m- q
- if ((((uint32_t)GPIO_InitStruct->GPIO_Mode) & ((uint32_t)0x10)) != 0x00)//如果GPIO_Mode[4]为1,表示为输出模式;8 x' w9 z l7 e
- {
: W- M" I: H4 x5 x - /*if(输出模式),将CNF[1:0]和MODE[1:0]的信息保存到currentmode[3:0]*/
) x& F% X# O2 t! V - assert_param(IS_GPIO_SPEED(GPIO_InitStruct->GPIO_Speed));
4 G/ _2 {1 H# P x' | D9 k) F - currentmode |= (uint32_t)GPIO_InitStruct->GPIO_Speed;//currentmode或上GPIO_Speed[1:0];
& z# d- k( _9 T5 t j* g4 W - }
2 \6 o$ P. V: k2 s - 0 ^& K+ m* S& r5 S/ o
- /*以下部分为CRL Configuration*/3 Y) m/ b$ U# s' [4 W$ T! Z
- if (((uint32_t)GPIO_InitStruct->GPIO_Pin & ((uint32_t)0x00FF)) != 0x00) //if(是低8位pin);/ s% [' `2 V7 |5 u0 T+ D, ]
- {
: V- T" f( @; E3 \ - tmpreg = GPIOx->CRL; //temreg存放CRL寄存器的信息;
: E W i2 A4 { - for (pinpos = 0x00; pinpos < 0x08; pinpos++) //pinpos为几,表示引脚几; - S+ E1 N& G; t3 }
- {( I+ }5 R: `, X+ a6 U
- pos = ((uint32_t)0x01) << pinpos;
8 e3 s/ q6 ?. O: h+ r* { - currentpin = (GPIO_InitStruct->GPIO_Pin) & pos; //currentpin:要么为当前pin值,要么为0;# K K6 I* `! \6 M% g! g, m3 a! F1 c
- if (currentpin == pos)
8 S8 l: r H8 w2 p2 t0 ?) B - {
1 A7 m& u! P i+ W/ e - pos = pinpos << 2; //pos:引脚对应的CRL配置位" S4 g% ^& U0 u5 R1 I$ L! v; N
- pinmask = ((uint32_t)0x0F) << pos; //pinmask:引脚对应的CRL[3:0]置18 m. q- @$ P, K2 K
- tmpreg &= ~pinmask; //temreg中对应引脚的[3:0]清05 Y( l$ R8 B& X4 r
- tmpreg |= (currentmode << pos); //temreg中对应引脚的[3:0]配置成currentmode[3:0]6 b! Z1 e- j) y. c4 Z5 s$ ?
- 6 ]" u4 U5 n& g( H" x5 E( Y
- //此处的if else应该是通过ODR来配置硬件,由中文参考手册8.1.7原理图推测可知 7 w& r. P- d; \5 E8 n1 i
- if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD) //if(输入连下拉电阻)" P3 K4 Q5 q ?3 J$ R
- {8 j8 a- r( R4 G7 R# ^, A6 w+ D; t3 t
- GPIOx->BRR = (((uint32_t)0x01) << pinpos); //通过配置ODR的16bit,对应pin的bit置0,连接下拉电阻;5 J- w6 |3 g2 d0 u( K7 d
- }
0 G5 L2 w% s0 X* Y" O( M, r1 ?" O - else
: S: t( W4 }8 o) ] - {
2 ?* | c) j% J& N% s - if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)//if(输入连上拉电阻)0 Y6 \+ i g; R
- {; U* J' P% _' i3 u3 ]0 e
- GPIOx->BSRR = (((uint32_t)0x01) << pinpos); //通过配置ODR的16bit,对应pin的bit置1,连接上拉电阻;1 C$ ?0 z8 O) u9 U9 o
- }/ g$ j( j! ]1 G4 n! C% p6 @
- }0 l9 B% C3 C" y3 v
- }: W6 J% V1 ]+ F. O+ c
- }
! I" z$ R$ T+ t* i% t - GPIOx->CRL = tmpreg; //把配置好对应pin脚的temreg放回CRL中
) k! U* f, y6 a1 W% R - }
7 N, W( m: j2 S( s8 L1 A -
3 G7 u# i- k* I% w# S" _ - /*---------------------------- GPIO CRH Configuration ------------------------*/- D' W& n! g% H' H4 R# p9 u
- /* Configure the eight high port pins */, V/ Q; l: t) r$ T" @
- if (GPIO_InitStruct->GPIO_Pin > 0x00FF)
8 c0 M+ Y- h) l/ `6 r - {
B; r! z6 V. R0 k - tmpreg = GPIOx->CRH;
$ a! U1 y+ `) Z- f" s) x - for (pinpos = 0x00; pinpos < 0x08; pinpos++)
! a% J8 g( d! a/ t1 o$ t3 K1 {' d - {
1 [5 ?! B4 |6 G3 c - pos = (((uint32_t)0x01) << (pinpos + 0x08));9 t6 |7 M, P( A) z4 v/ C
- /* Get the port pins position */& K7 c! D! `7 w' Q5 w; C! U
- currentpin = ((GPIO_InitStruct->GPIO_Pin) & pos);4 p' q6 y8 h9 L3 k6 \
- if (currentpin == pos)* n( A+ t) L; O6 K( F' q
- {
: R( [$ S3 M2 k# M" L9 H - pos = pinpos << 2;
- [+ V2 |# M1 Y' f5 {* @' K - /* Clear the corresponding high control register bits */
; ^1 O! Q7 ]! i8 a3 I7 r - pinmask = ((uint32_t)0x0F) << pos;+ W9 [, O1 v: r7 Y% \2 q0 U
- tmpreg &= ~pinmask;
3 T o3 F/ f0 N0 y2 H - /* Write the mode configuration in the corresponding bits */
( \7 M+ p8 w6 Z% H& A5 J - tmpreg |= (currentmode << pos);
- V8 _. |" d8 G" C1 M - /* Reset the corresponding ODR bit */
: `; K+ C+ W3 E- z - if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPD)& U8 J2 H3 [) b; q. d
- {
* z9 c n0 X* o6 q+ _ - GPIOx->BRR = (((uint32_t)0x01) << (pinpos + 0x08));
2 ?6 V7 A* s. ^+ N1 ~, R - }
; D! o0 \+ M' C" L$ d - /* Set the corresponding ODR bit */
: J, {8 b4 A8 ?" W' l1 h7 n8 u - if (GPIO_InitStruct->GPIO_Mode == GPIO_Mode_IPU)
" O& @+ i6 q) `2 u4 @* S5 z8 t - {
. C8 X3 ~/ V0 Q - GPIOx->BSRR = (((uint32_t)0x01) << (pinpos + 0x08));% h1 j" p v2 t0 l
- }" \5 ~- a' n( F$ _
- }
. L1 A0 o; c6 c& @7 K' q% }) z - }5 Q+ B: \+ j- B$ C
- GPIOx->CRH = tmpreg;
8 h+ U C5 h) Q4 [) N - }
+ V9 d) M2 J6 ]9 U: K' `2 P - }
复制代码
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
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
2.3.1 IDR寄存器的使用函数 - /*以下代码位于stm32f10x_gpio.c中*1 读取IDR寄存器某一位的值,即pin值;*2 读取IDR寄存器的值,即port值;*/
0 D( V6 a9 k# n/ c, d - uint8_t GPIO_ReadInputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
9 ~6 ?: P9 ~8 j+ N: w- a; j$ C" f; V1 o - {
, C8 h3 Y+ V9 H - uint8_t bitstatus = 0x00;8 d( y+ t/ w+ | G
- . m6 C% T, s- s- p
- /* Check the parameters */' ~7 a; j/ l* Z3 B
- assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
7 Y/ M( f: S+ {$ l( g - assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
5 H6 J8 ^4 W7 _, r+ ^7 O - , F8 J3 O; {9 }* u, y/ L
- /*先读取整个IDR寄存器,然后通过&来读取bit,IDR寄存器的最小处理单位是16bit*/6 C5 l0 E+ f( C/ ?0 l
- if ((GPIOx->IDR & GPIO_Pin) != (uint32_t)Bit_RESET)9 D& y8 _4 s( z
- {3 g; j, a5 _( t3 A1 U
- bitstatus = (uint8_t)Bit_SET;
6 ]+ {, E% a- S% E! Y; m - }
2 I2 k6 ~! I: |" U% G- A$ u - else% X4 l7 I( K; _
- {1 }5 u& r+ v q: I
- bitstatus = (uint8_t)Bit_RESET;
% H( A) }3 \6 a& P" a8 H; ^; e( m - }% }: }) q$ P! s7 y) M# x0 n0 e1 @. J4 F
- return bitstatus;
h- N: \/ ]' m3 k% m; f: v - }# ]& {, _6 d3 t' a4 |
- ' v1 P7 W* ~' B% `3 }' M7 y- I4 C9 I
- uint16_t GPIO_ReadInputData(GPIO_TypeDef* GPIOx)
/ \) T4 Y$ ~# x, i1 V+ C: a: @ - {
. B6 [0 z" Z& }6 A - /* Check the parameters *// P9 T( F2 E' l" q k# Q1 ^
- assert_param(IS_GPIO_ALL_PERIPH(GPIOx));; Z+ i1 h' j7 k0 A* j( d& M
-
' J1 c: J: U- N5 Y' l! w8 x - return ((uint16_t)GPIOx->IDR);
! P1 J+ W7 K* U( V1 B* f - }
复制代码 2.3.2 ODR寄存器的使用函数 - /*以下代码位于stm32f10x_gpio.c中;
2 y: |+ ?" L! G, x8 ? - *1 读取ODR寄存器某一位的值;即读取pin值;& I6 \9 U% @1 [$ X) Y5 k- b
- *2 读取ODR寄存器的值;即读取port值;
9 h2 D+ L) L# @* }3 c - *3 向ODR寄存器写入port值;*/- S: D: T2 d4 a8 Z. D1 `
- uint8_t GPIO_ReadOutputDataBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
. {3 g2 g8 f o( J2 `$ w - {
% ^ z3 Q+ V' f/ [+ R# {. c - uint8_t bitstatus = 0x00;6 u+ e1 P. J: {1 v; g4 m; X
- assert_param(IS_GPIO_ALL_PERIPH(GPIOx));1 E. Q' {8 A. T- w; q6 X- {9 u
- assert_param(IS_GET_GPIO_PIN(GPIO_Pin)); 1 ?5 a4 l0 P# q {+ ]- y5 S
- 2 }, f' |3 X7 A( c& K+ L7 s0 S% ?
- /*先读取整个ODR寄存器,然后通过&来读取bit,ODR寄存器的最小处理单位是16bit*/
& C6 G$ ^* L7 u3 b( _ - if ((GPIOx->ODR & GPIO_Pin) != (uint32_t)Bit_RESET){3 t3 V+ J$ u) z& l+ V
- bitstatus = (uint8_t)Bit_SET;" l- }+ g) ~1 X' {3 f
- }6 G( Z6 |3 c3 N& W2 {
- else{
* Y; g3 B! T! n - bitstatus = (uint8_t)Bit_RESET;
* g9 r! R: p. e5 E* |9 e8 q9 Q - }( c3 {# S/ D" }
- return bitstatus;7 e! W! @& Z8 z
- }
" |0 {! n* r& @' O0 t9 h
: E$ R3 L# ^( T0 V9 b- uint16_t GPIO_ReadOutputData(GPIO_TypeDef* GPIOx)! r& s8 ?) U, C# U8 ^
- {9 y" ~- M9 J9 F+ \. w: p$ I+ g: P) r
- assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
- E1 o' M$ g% P - return ((uint16_t)GPIOx->ODR);" Y: I/ a6 b% Y/ Q& \: z
- }. j$ V5 [- @% E
- G8 v' D; G8 O+ D0 b/ Q" u- void GPIO_Write(GPIO_TypeDef* GPIOx, uint16_t PortVal)5 G L8 y$ O! N! i
- {
, U. ~2 v! v+ m& Q: h$ E - assert_param(IS_GPIO_ALL_PERIPH(GPIOx));+ j: e% o: ]$ ?; A
- GPIOx->ODR = PortVal;8 P/ } ?( U9 M2 s* O3 Q
- }
复制代码 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
2.4.1 BSRR寄存器:pin置1 - void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
" ]4 a7 v0 Q$ B7 W - {
+ t, r" H- Z2 X - assert_param(IS_GPIO_ALL_PERIPH(GPIOx));
4 @8 b0 O0 q' _ - assert_param(IS_GPIO_PIN(GPIO_Pin));
$ a% P3 }/ x' ^6 M5 m" I. e -
7 C$ M+ O% d" J+ _ - GPIOx->BSRR = GPIO_Pin;5 M q1 e; v- B0 S2 s
- }
复制代码
% N8 C: w% S1 I+ }, `2 k* b$ @# W 2.4.2 BRR寄存器:pin清0 - void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin)
7 v( f: o. Z, r9 z3 e - {$ F2 h9 f+ r. c2 \9 @
- assert_param(IS_GPIO_ALL_PERIPH(GPIOx));% }( c8 ]1 F/ k
- assert_param(IS_GPIO_PIN(GPIO_Pin));
: s) C0 L- E7 {: G; q - % C: ]6 Y- Q0 q, N- T
- GPIOx->BRR = GPIO_Pin;
- M' q4 r3 C& D# `3 o* P1 Q - }
复制代码 0 z9 ^7 c( @7 i
2.4.3 设置pin脚相应的数值:pin的置1清0 - void GPIO_WriteBit(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin, BitAction BitVal)
3 [" ~% j* Y9 Q - {
6 ~% {* d% R- _# f - assert_param(IS_GPIO_ALL_PERIPH(GPIOx));3 a$ S" @3 t' F/ F' ^3 c+ k
- assert_param(IS_GET_GPIO_PIN(GPIO_Pin));
# _7 _1 G, d6 Z+ ? - assert_param(IS_GPIO_BIT_ACTION(BitVal)); + Z/ v5 f% ^& }: N f
- % c y/ b7 q$ p% S; l
- if (BitVal != Bit_RESET)
3 g0 x. }! M3 m: E$ C) D - {
! M6 V b; v$ Y$ p3 L l/ c - GPIOx->BSRR = GPIO_Pin;% l2 D1 T0 T; U x2 k( n e
- } \3 ~( v% A6 o D* d5 O7 o; f2 M- L
- else1 o. d r6 Y. X8 R+ ?/ v6 ]
- {
4 F# h8 Q. P5 C% ^/ ~7 z. r - GPIOx->BRR = GPIO_Pin;
2 W- r: c! Q7 |; T- ^! T) U - }2 |9 Y) a* @4 ~4 y0 V( }6 j; v+ s
- }
复制代码 $ 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来设置; - #include "delay.h"
& E. f% t2 |: W" l1 t
( P0 q( A" f P1 ]- int main(void)
% E5 t' F+ X' M) q; [ - { # r, E( j) a; x+ M" I. J
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);- i) p9 K3 J: ^8 z* A& {$ }5 y* `
- GPIO_InitTypeDef GPIO_Struct;
. u4 j! z1 N' i. o8 `
: }) l& d2 z7 Q- //portA pin1 as input; 9 T2 p* x; @6 m
- GPIO_Struct.GPIO_Pin = GPIO_Pin_1; & Q. z# @2 Q& a: s3 Z# t6 R
- GPIO_Struct.GPIO_Mode =GPIO_Mode_IPD ; 1 m/ Y# d! F9 V9 p/ Z# Y
- GPIO_Init(GPIOA, &GPIO_Struct); ) S- W; M2 S% h {$ L0 M
-
5 V: [7 ]: r0 b - //portA pin2 as output;
9 u$ k* y- |- Z* A: t - GPIO_Struct.GPIO_Pin = GPIO_Pin_2; ) ~! s/ S7 g0 u. N# m- x0 i
- GPIO_Struct.GPIO_Speed = GPIO_Speed_50MHz;6 |" }6 B) ^! n8 ]
- GPIO_Struct.GPIO_Mode = GPIO_Mode_Out_PP;
0 v. |: g( M' @ - GPIO_Init(GPIOA, &GPIO_Struct); ) L* C( E& G! Y( y) R
- while(1)
7 y- u9 S4 q9 ?- j7 u - {
7 q4 E$ q# c' B1 [" I( F - GPIO_SetBits(GPIOA,GPIO_Pin_2);
. k" e4 F, e% `; Z1 h - delay_ms(100);5 w4 C" f, c$ O v
- GPIO_ResetBits(GPIOA,GPIO_Pin_2);
l+ H) z8 F; s6 c: F/ g - delay_ms(100);
7 Q- Q+ i- }& b6 I8 {- d - } 6 a" O, X4 u9 r2 z
- }
复制代码 0 F/ K# I7 n! y+ f/ Z ^
5 小结 本文主要是结合通用GPIO口的寄存器,对标准库中通用GPIO口的代码进行了分析和概括; 然后提供了一个代码示例;如果只是使用而不想理解标准库原理,则直接使用接口函数即可,如代码示例所示;
0 x5 X8 j( L2 X6 u) ` |