|
本帖最后由 zhdzhd-174422 于 2018-12-31 21:24 编辑 & T1 v0 S D7 _; L4 H 第8章 使用寄存器点亮LED灯/ Z2 @& U( `" n; u1 M 本章实验点亮2组GPIO端口中的2个LED灯。
0 L5 a, r4 _+ y2 G d STM32F103ZET6* n" t' M" o; Y6 o! K0 e9 `5 x; q - 一共有144脚 - 一共有7组IO口# A; N' u- Z2 W( i) b6 L# } - 每组IO口有16个IO - 一共16X7=112个IO,分别是GPIOA,GPIOB...GPIOG。4 Z/ w% v7 q2 I* } 4 m3 d8 C2 _( E! Q2 l7 p$ c( Y
1 i) q3 f1 \4 b& O4 h/ j GPIO 有8 种工作模式,分为4种输入模式和4种输出模式,用代码表示如下:5 b L: B. J# B( e$ W5 n; e0 j- {1 a V ----------------------------------------------------------------------------------------------------- typedef enum { GPIO_Mode_AIN = 0x0, // 模拟输入: b/ ^9 K/ ?" w# I9 R% x# t GPIO_Mode_IN_FLOATING = 0x04, // 浮空输入! s# c9 g }1 Q GPIO_Mode_IPD = 0x28, // 下拉输入 GPIO_Mode_IPU = 0x48, // 上拉输入 GPIO_Mode_Out_OD = 0x14, // 开漏输出 GPIO_Mode_Out_PP = 0x10, // 推挽输出& i0 V2 \; L6 p- R, X0 @ y5 C+ b GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出 GPIO_Mode_AF_PP = 0x18 // 复用推挽输出 } GPIOMode_TypeDef; % H* B; s8 J- J# l9 f* e ---------------------------------------------------------------------------------------------------- 有3种最高切换频率: -2MHZ -10MHz -50MHz 6 X3 C' u: P' W( N2 C3 m7 G# O 每组IO口包含7个寄存器,可以控制GPIO的16个IO口。 # v& }7 `5 j7 o1 C! m, S- M. r2 ?. C a - GPIOx_CRL 端口配置低寄存器 - GPIOx_CRH 端口配置高寄存器- l% w! Q$ @1 ]2 ]4 |) m - GPIOx_IDR 端口输入寄存器5 ~1 {7 k' z, x! @6 p( P - GPIOx_ODR 端口输出寄存器 - GPIOx_BSRR 端口位设置/清除寄存器 - GPIOx_BRR 端口位清除寄存器 - GPIOx_LCKR 端口配置锁存寄存器 9 M, e8 X+ V' Y" b4 f5 C4 h 用C语言把上面的寄存器地址转换成指针代码如下:(x代表A---G) ----------------------------------------------------------------------------------------------------------------------6 d- m+ x5 y' D( T. n9 C #define GPIOx_CRL *(unsigned int*)(GPIOB_BASE+0x00) #define GPIOx_CRH *(unsigned int*)(GPIOB_BASE+0x04)' _6 J4 l5 K* 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), }' u$ V# \7 F6 a: t/ l #define GPIOx_BRR *(unsigned int*)(GPIOB_BASE+0x14) #define GPIOx_LCKR *(unsigned int*)(GPIOB_BASE+0x18) ---------------------------------------------------------------------------------------------------------------------- GPIOB_BASE从前面一章的学习我们知道是GPIO的外设基地址,而GPIO都是挂载到APB2总线上的,故各端口的基地址代码如下: ---------------------------------------------------------------------------------------------------------------------- #define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) #define GPIOC_BASE (APB2PERIPH_BASE + 0x1000)* Z4 Z; G7 C7 o j; N #define GPIOD_BASE (APB2PERIPH_BASE + 0x1400)6 k- M8 j6 }% m3 h7 l #define GPIOE_BASE (APB2PERIPH_BASE + 0x1800) #define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00)3 q9 b, t: F" q8 F #define GPIOG_BASE (APB2PERIPH_BASE + 0x2000)/ y% n: g* ~- \: A , L1 r2 e" J* m2 \ ---------------------------------------------------------------------------------------------------------------------- 我用的是原子的精英板,板载有2个LED,分别是LED0和LED1,对应芯片的PB5和PE5脚。
/ h0 A* y$ `/ N6 z$ C7 } 所有的 GPIO都挂载到 APB2 总线上,具体的时钟由 APB2外设时钟使能寄存器(RCC_APB2ENR)来控制,见下图:$ B- Q* L0 |# `. Q1 ]1 a6 m+ ` % E$ n1 E. s: A. c% z' q. {0 F
开启时钟端口的代码如下: " d! Y+ O y1 X% n, O0 |# O4 [ RCC_APB2ENR |= (1<<3); //开启GPIOB端口的时钟 RCC_APB2ENR |= (1<<6); //开启GPIOB端口的时钟 3 u7 }( T9 w# j' O) S 在输出模式时,对端口位设置/清除寄存器 BSRR 寄存器、端口位清除寄存器 BRR 和ODR 寄存器写入参数即可控制引脚的电平状态,其中操作 BSRR 和 BRR 最终影响的都是ODR 寄存器,然后再通过 ODR寄存器的输出来控制 GPIO,直接操作 ODR寄存器来控制 GPIO 的电平。代码如下: & U, y1 M6 {0 z7 D+ J GPIOB_ODR &= ~(1<<0);4 H* y3 y+ E2 D9 A GPIOE_ODR &= ~(1<<0);
6 Q- r* \! U3 r, \4 c: h0 D U 通过上面 配置引脚模式,开启时钟,控制引脚电平这三步,我们实现了控制2个 LED 的基本步骤和代码,另外有个需要注意的事项就是: 我们在 main中添加如下函数: ----------------------------------------------------------------------------------------------------------------------, Z- C2 m! W& M) f // 函数为空,目的是为了骗过编译器不报错! j: ~0 n/ ~, r( z( M- J void SystemInit(void)$ y1 k8 V" y) `5 {( w {' p. J; @+ w0 U5 F6 u# d }4 o# l; G+ D' [" X6 t ----------------------------------------------------------------------------------------------------------------------4 q; D( Z" J. }2 L- u2 @+ z) ? 如果不添加上面的函数,编译时会出现如下错误: “Error: L6218E: Undefined symbol SystemInit (referred from startup_stm32f10x.o)”2 [5 y/ ^6 O: d/ v 错误提示 SystemInit 没有定义。 从分析启动文件时我们知道,Reset_Handler 调用了该函数用来初始化 SMT32 系统时钟,为了简单起见,我们在 main 文件里面定义一个SystemInit 空函数,什么也不做,为的是骗过编译器,把这个错误去掉。关于配置系统时钟我们在后面再写。当我们不配置系统时钟时,STM32 会把 HSI 当作系统时钟,HSI=8M,由芯片内部的振荡器提供。这时再编译就没有错了,完美解决。+ [. r; w7 K" D Y# U- l 还有一个方法就是在启动文件statrup_stm32f10x.hd.s中把有关SystemInit 的代码注释掉也可以(首先要把此文件的“只读”属性取消,在下面红色的语句前面增加“;”即可注释掉,绿色保留)。 ; Reset handler5 s- s! [* H7 u1 a# R7 q9 D8 m Reset_Handler PROC EXPORT Reset_Handler [WEAK] IMPORT __main IMPORT SystemInit LDR R0, =SystemInit BLX R0 LDR R0, =__main BX R0 ENDP * d/ |/ ?2 H7 j7 z+ [4 x
, A+ B- T- @! M: Y 如果是初学,还是建议在main.c中增加void SystemInit(void)函数比较方便,不要去动启动文件。 1 o2 r# c/ N& N" }/ Q" `3 l2 @ 现在完整组织一下用 STM32 控制2个 LED 的代码: -------------------------------------------------------------------------------------------------------------- int main(void)& P/ ^1 o: ~2 S7 F {' x( ]5 h: h$ h7 D# k r6 [1 s RCC_APB2ENR |= (1<<3); //开启 GPIOB 端口时钟, Q" W. @: p, x$ V; h9 z- v. {6 N RCC_APB2ENR |= (1<<6); //开启 GPIOE 端口时钟" K, G+ t+ w- [$ v6 W4 y' T: ] ( E% ?5 t& s2 j' h GPIOB_CRL &= ~( 0x0F<< (4*5)); //清空控制 PB5 的端口位 GPIOB_CRL |= (1<<4*5); //配置 PB5 为通用推挽输出,速度为 10M+ I1 V! J; p* Q5 n GPIOB_ODR |= (0<<0); //PB5 输出 低电平5 p8 I( J6 p- W( k" h r+ p GPIOE_CRL &= ~( 0x0F<< (4*5)); //清空控制 PE5 的端口位& A. S' `1 f) }/ |* o3 K GPIOE_CRL |= (1<<4*5); //配置 PE5 为通用推挽输出,速度为 10M GPIOE_ODR |= (0<<0); //PE5 输出 低电平1 y. F# G# d. s0 r1 h 1 P1 Y1 Q& G) X% E while (1); } //切记此处要加回车) g- Z, }6 Q8 D9 i2 R; {: M ! ~7 a" z0 D2 K6 d5 t void SystemInit(void) : Y9 v9 }% g- z7 s5 G+ D { } //切记此处要加回车 ---------------------------------------------------------------------------------------------------------------------- 将上面的代码写入上章创建的空的main.c中。( l; }/ F5 [# G! N W1 ]
0 k: J* Q8 b3 u @2 I+ X 将上面配置GPIOB和GPIOE寄存器要使用的代码写入上次创建的空文件stm32f10x.h中:# U9 f7 A# O# z6 i- V ---------------------------------------------------------------------------------------------------------------------- - L* E7 ^, O* ~' J # B; {& Q8 [3 Q, ~ #define PERIPH_BASE ((unsigned int)0x40000000) #define APB1PERIPH_BASE PERIPH_BASE #define APB2PERIPH_BASE (PERIPH_BASE + 0x10000)6 B& r# j6 e; ~2 v2 J1 D- H! z1 g #define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) ! ?( h. X$ L& b5 z4 Z & C6 ^4 H# q+ R #define RCC_BASE (AHBPERIPH_BASE + 0x1000) #define RCC_APB2ENR *(unsigned int*)(RCC_BASE + 0x18) 5 B. `+ v8 r7 d" j" _- |7 w #define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00) #define GPIOE_BASE (APB2PERIPH_BASE + 0x1800)8 ~* j6 V. }4 _; { #define GPIOB_CRL *(unsigned int*)(GPIOB_BASE + 0x00)4 _) Q5 ~. Y, j5 |- U #define GPIOB_ODR *(unsigned int*)(GPIOB_BASE + 0x0C) 3 w; B1 D9 ?% r. u+ O #define GPIOE_CRL *(unsigned int*)(GPIOE_BASE + 0x00) #define GPIOE_ODR *(unsigned int*)(GPIOE_BASE + 0x0C) ~* K" S2 O# ~- ~0 f* ^6 ? l , A* b5 r. f- G' J' u- U2 m ----------------------------------------------------------------------------------------------------------------------; k$ U/ Q5 ?. C( v( |
编译通过,见下图: % z6 [) N( o: q/ K! q. d$ u: p. F
" u# t/ b6 ^% q5 S 下载测试,效果如下:
眼看2019年就要来了,2018马上要悄悄的离我们而去了,回想今年一年在论坛混的日子,感觉收获颇丰,感谢各位管管和坛友们的支持和帮助,祝大家2019年工作顺利,财运恒通,身体健康!祝论坛在2019年越办越旺,给广大坛友带来更多的福利!!谢谢!0 n2 u! Y0 A. c0 V8 d # H- x% ?( d+ c ( Q# q$ ?# f3 ] ) z \7 U; l4 V7 z |
谢谢分享 |
基本讲解 |
| 学得挺认真的嘛 |
| 占个楼 |
|
学习学习. L) j4 H, d0 G9 | |
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之继电器模块
微信公众号
手机版