|
本帖最后由 zhdzhd-174422 于 2018-12-31 21:24 编辑 ! m+ e! T, q/ S1 [* c 第8章 使用寄存器点亮LED灯 本章实验点亮2组GPIO端口中的2个LED灯。
STM32F103ZET6 - 一共有144脚 - 一共有7组IO口 - 每组IO口有16个IO - 一共16X7=112个IO,分别是GPIOA,GPIOB...GPIOG。 7 [! [: W5 e+ c5 L5 ^& o/ ?
GPIO 有8 种工作模式,分为4种输入模式和4种输出模式,用代码表示如下:# V5 u' T. c; } ----------------------------------------------------------------------------------------------------- typedef enum {7 n0 h, V6 j, A! ^7 z% j6 j$ Y& ] GPIO_Mode_AIN = 0x0, // 模拟输入 GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入8 r; B1 R( c3 P+ j5 w @7 g GPIO_Mode_IPD = 0x28, // 下拉输入6 l* Z2 S: `* G) h' @: k% e GPIO_Mode_IPU = 0x48, // 上拉输入 : c0 z3 t6 H( ~$ Y$ P. e+ \' s GPIO_Mode_Out_OD = 0x14, // 开漏输出+ q6 M0 g3 C/ H2 j9 ^. ] GPIO_Mode_Out_PP = 0x10, // 推挽输出* ]( W* c% P3 J7 o [ GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出 GPIO_Mode_AF_PP = 0x18 // 复用推挽输出. t) B) W, `* Y( S% J+ N } 8 d7 c& ^, D2 d5 U2 {4 L% R1 H GPIOMode_TypeDef;4 Z- \( x* D6 r8 Y* w ----------------------------------------------------------------------------------------------------6 P' t0 N5 X" n4 }1 K1 k1 L6 A, }4 Y 8 ?& i* h( Z$ w- c3 N- A 有3种最高切换频率: [" R: ~; z) V0 I -2MHZ -10MHz -50MHz" `* h5 t) |% Z: U4 q6 l 每组IO口包含7个寄存器,可以控制GPIO的16个IO口。 n6 ]3 C2 u* K 6 g; e! X: `( N3 G" ~6 S: `, `4 C - GPIOx_CRL 端口配置低寄存器 - GPIOx_CRH 端口配置高寄存器9 o& t/ C/ L) C2 U - GPIOx_IDR 端口输入寄存器 - GPIOx_ODR 端口输出寄存器% ]2 v% n: m+ e* e5 q3 Q - GPIOx_BSRR 端口位设置/清除寄存器 - GPIOx_BRR 端口位清除寄存器2 ~/ Q6 h8 r, @* D - GPIOx_LCKR 端口配置锁存寄存器 用C语言把上面的寄存器地址转换成指针代码如下:(x代表A---G)7 m2 X L9 ]8 n* ^ ---------------------------------------------------------------------------------------------------------------------- #define GPIOx_CRL *(unsigned int*)(GPIOB_BASE+0x00)2 d$ ?, X3 D9 e #define GPIOx_CRH *(unsigned int*)(GPIOB_BASE+0x04)) B- c& k1 @% w( R #define GPIOx_IDR *(unsigned int*)(GPIOB_BASE+0x08) #define GPIOx_ODR *(unsigned int*)(GPIOB_BASE+0x0C) #define GPIOx_BSRR *(unsigned int*)(GPIOB_BASE+0x10) #define GPIOx_BRR *(unsigned int*)(GPIOB_BASE+0x14) #define GPIOx_LCKR *(unsigned int*)(GPIOB_BASE+0x18)' Z2 O% s+ T7 F Y9 }# |6 Z ----------------------------------------------------------------------------------------------------------------------0 ^* [, H1 [0 M1 A% o/ { GPIOB_BASE从前面一章的学习我们知道是GPIO的外设基地址,而GPIO都是挂载到APB2总线上的,故各端口的基地址代码如下:7 C Y- W6 I9 G% M ---------------------------------------------------------------------------------------------------------------------- #define GPIOA_BASE (APB2PERIPH_BASE + 0x0800)3 b5 b$ U( \3 X; X/ M1 B #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)1 r' ~0 V( g3 K. _ #define GPIOC_BASE (APB2PERIPH_BASE + 0x1000) #define GPIOD_BASE (APB2PERIPH_BASE + 0x1400) #define GPIOE_BASE (APB2PERIPH_BASE + 0x1800) #define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)- { _' Y/ ?. @7 I A% A #define GPIOG_BASE (APB2PERIPH_BASE + 0x2000) ---------------------------------------------------------------------------------------------------------------------- 我用的是原子的精英板,板载有2个LED,分别是LED0和LED1,对应芯片的PB5和PE5脚。
8 C/ n* n8 L$ |5 [: A ( u) Z0 _4 H9 \) H" _ 所有的 GPIO都挂载到 APB2 总线上,具体的时钟由 APB2外设时钟使能寄存器(RCC_APB2ENR)来控制,见下图:( k' Z& H8 }0 _0 f; r. c$ p2 u
7 d& T5 _) k* F9 ` % B# ]2 g$ }6 W4 V 开启时钟端口的代码如下:# j% P1 _) Y& ^ 0 @5 X7 W# B# g: ^* P5 k RCC_APB2ENR |= (1<<3); //开启GPIOB端口的时钟- f8 K ~0 t" n 9 r& D/ m' W& A3 ]# D RCC_APB2ENR |= (1<<6); //开启GPIOB端口的时钟 在输出模式时,对端口位设置/清除寄存器 BSRR 寄存器、端口位清除寄存器 BRR 和ODR 寄存器写入参数即可控制引脚的电平状态,其中操作 BSRR 和 BRR 最终影响的都是ODR 寄存器,然后再通过 ODR寄存器的输出来控制 GPIO,直接操作 ODR寄存器来控制 GPIO 的电平。代码如下:" ^! n. S- s C, \& _ 8 |: V* h0 ^# g, Y; M8 F GPIOB_ODR &= ~(1<<0);& H( ^- F1 c! B! ]( ], c2 y GPIOE_ODR &= ~(1<<0); . b0 O" o. |4 E( y5 G
通过上面 配置引脚模式,开启时钟,控制引脚电平这三步,我们实现了控制2个 LED 的基本步骤和代码,另外有个需要注意的事项就是: 我们在 main中添加如下函数:$ H4 b4 i# ~4 ?. ^2 C4 y) M" R5 g" y ---------------------------------------------------------------------------------------------------------------------- // 函数为空,目的是为了骗过编译器不报错 void SystemInit(void) { } 7 U0 h J7 o3 W ---------------------------------------------------------------------------------------------------------------------- 如果不添加上面的函数,编译时会出现如下错误: “Error: L6218E: Undefined symbol SystemInit (referred from startup_stm32f10x.o)”" I7 o! g; |& ?9 ~4 U 错误提示 SystemInit 没有定义。+ w5 E! ~$ s6 ^6 D 从分析启动文件时我们知道,Reset_Handler 调用了该函数用来初始化 SMT32 系统时钟,为了简单起见,我们在 main 文件里面定义一个SystemInit 空函数,什么也不做,为的是骗过编译器,把这个错误去掉。关于配置系统时钟我们在后面再写。当我们不配置系统时钟时,STM32 会把 HSI 当作系统时钟,HSI=8M,由芯片内部的振荡器提供。这时再编译就没有错了,完美解决。 还有一个方法就是在启动文件statrup_stm32f10x.hd.s中把有关SystemInit 的代码注释掉也可以(首先要把此文件的“只读”属性取消,在下面红色的语句前面增加“;”即可注释掉,绿色保留)。! a* u0 z k0 K- k 6 B# y2 H; s! ]# s& b5 x; c% K ; Reset handler Reset_Handler PROC. V9 `. u3 I$ \. W8 F EXPORT Reset_Handler [WEAK]* p0 b/ o0 j# c( z$ f' P* u IMPORT __main IMPORT SystemInit LDR R0, =SystemInit BLX R0 LDR R0, =__main/ T O% S4 @& f# I BX R0; ]% Z' s o) D- z3 ?& F8 o8 ]! p ENDP( q( j9 z0 C) m; U; r' N
5 G) E8 q3 q0 L j 如果是初学,还是建议在main.c中增加void SystemInit(void)函数比较方便,不要去动启动文件。 现在完整组织一下用 STM32 控制2个 LED 的代码:; U( N! V2 x0 C L' w --------------------------------------------------------------------------------------------------------------+ k8 ^- W* l$ w2 q7 _; y; P0 |# D int main(void)4 t8 P6 I9 y" R6 p1 O9 w; ]9 }% n {9 W: N! j: F" ^: I# h# P2 e RCC_APB2ENR |= (1<<3); //开启 GPIOB 端口时钟 2 G" |, e! i8 n6 C. U6 Z7 j6 f RCC_APB2ENR |= (1<<6); //开启 GPIOE 端口时钟 GPIOB_CRL &= ~( 0x0F<< (4*5)); //清空控制 PB5 的端口位6 g8 n7 C& {0 s GPIOB_CRL |= (1<<4*5); //配置 PB5 为通用推挽输出,速度为 10M GPIOB_ODR |= (0<<0); //PB5 输出 低电平4 _. S- v# P! C" X1 g ; I, p8 V7 Z; M; v* Y3 w6 ` GPIOE_CRL &= ~( 0x0F<< (4*5)); //清空控制 PE5 的端口位 GPIOE_CRL |= (1<<4*5); //配置 PE5 为通用推挽输出,速度为 10M GPIOE_ODR |= (0<<0); //PE5 输出 低电平 while (1); } //切记此处要加回车 void SystemInit(void) {4 D* O2 g# y0 i3 Q% Q" ` } //切记此处要加回车, L, \- A# E2 ~- M " R: U( T" k0 _' k& G: u5 @5 e ' p5 ^2 l% ?- p0 T " t! n( c% {4 z( ] ---------------------------------------------------------------------------------------------------------------------- 将上面的代码写入上章创建的空的main.c中。$ D! x2 _/ G" I- I6 q! [) {( Y
将上面配置GPIOB和GPIOE寄存器要使用的代码写入上次创建的空文件stm32f10x.h中: ---------------------------------------------------------------------------------------------------------------------- 1 [/ r( \2 z* ? J; V #define PERIPH_BASE ((unsigned int)0x40000000)/ u5 p; j6 A0 {! K' z( [) @0 [" Q #define APB1PERIPH_BASE PERIPH_BASE" d# c4 g- I! j! e3 _$ A/ F9 ^ R #define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) #define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) #define RCC_BASE (AHBPERIPH_BASE + 0x1000)$ h% q7 g/ _1 S8 u+ o0 |( D #define RCC_APB2ENR *(unsigned int*)(RCC_BASE + 0x18) #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) #define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)7 `" l' V6 I2 l % [2 E( k. a6 y% L) v! D: P, J3 x #define GPIOB_CRL *(unsigned int*)(GPIOB_BASE + 0x00) #define GPIOB_ODR *(unsigned int*)(GPIOB_BASE + 0x0C) #define GPIOE_CRL *(unsigned int*)(GPIOE_BASE + 0x00) #define GPIOE_ODR *(unsigned int*)(GPIOE_BASE + 0x0C) ; j$ r6 x; n" w# |0 w ----------------------------------------------------------------------------------------------------------------------
/ z2 s( f7 I( n+ | 编译通过,见下图:
, K+ s+ ]9 n3 |9 l$ Q }! ]; n 下载测试,效果如下:$ V1 q" F' t) x7 L1 ~- Y % E9 z) i6 e; x
眼看2019年就要来了,2018马上要悄悄的离我们而去了,回想今年一年在论坛混的日子,感觉收获颇丰,感谢各位管管和坛友们的支持和帮助,祝大家2019年工作顺利,财运恒通,身体健康!祝论坛在2019年越办越旺,给广大坛友带来更多的福利!!谢谢! - Z) W, d; P$ @' ~" u. `9 l0 X 1 }" l2 Q5 m8 y# L ( Y9 b0 G. T0 R / z! d' i4 A/ K7 Z |
谢谢分享 |
基本讲解 |
| 学得挺认真的嘛 |
| 占个楼 |
|
学习学习2 h Y3 n* u; Q4 b# g a |
DMA 原理从 0 到 1:为什么它能解放 CPU?一文讲透 DMA 的工作流程
【福利三:雨露均沾·逢7狂欢】之五:STM32系统时钟PLL故障排除1例
【福利三:雨露均沾·逢7狂欢】之三:AI帮我查BUG
福利三:雨露均沾·逢7狂欢】之二:STM32F407VG串口通信乱码故障的排除
基于STM32F103的I2C主从机通信
OpenBLT移植到STM32F103战舰开发板上适用于所有STM32F103系列的Bootloader
TFT LCD 与 FSMC 的硬件连接大容量 STM32F10xxx FSMC 接口
单片机:初学者该了解的STM32F103基础知识
2025软件工具兔哥知道
STM32之继电器模块
微信公众号
手机版