在开始之前记得先准备好环境:
STM32F103核心板下载教程.pdf · 林何/STM32F103C8 - 码云 - 开源中国 (gitee.com)
一、STM32的GPIO模块数据手册详解
每个GPIO端口对应16个引脚,例GPIOA(PA0~PA15)
内核cpu就可以通过APB2总线对寄存器读写,完成输出电平和读取电平的功能
1.GPIO和APIO
GPIO:通用输入/输出IO
APIO:备用输入/输出IO
2.GPIO功能描述
3.配置模式
【STM32】GPIO工作原理(八种工作方式超详细分析,附电路图)-CSDN博客
1.输入浮动,输入上拉,输入下拉(数字状态)
都是数字模式,数字IO,只关心电平是1还是0
输入浮空:就是当电平输入进来后,直接输入到寄存器中【不对其进行操作】
输入上拉:在内部有一个上拉电阻(将电平拉高),只能外部接地,才能将电平拉低,如果TTL肖特基触发器:输入的是高电平或者干扰信号,则会将其过滤调。
2.模拟输入(模拟状态)
AD转换
可以工作在模拟电路或者数字电路
3.输出
开漏输出
推挽输出
4.备用
将不用的引脚配置成备用引脚(GPIO)才可以被使用
4.输出字节数控制
1)输出32位字:使用GPIO_CRL---效率低,但是兼容性好
2)输入小于32位字:使用CPIOx_BSRR--效率高,但是兼容性差
5.I/O 端口位框图
6.端口位配置表
7.输出模式位
8.外部中断/唤醒线
将IO口和相对应的中断映射起来
9.GPIO寄存器描述
1. 端口配置低寄存器(GPIOx_CRL)
由于每个GPIO口需要4位来进行配置输入输出模式(2位配置MODE,2位配置CNF),这样的话每组16个GPIO口则需要64位,这也就表明需要两个32位寄存器。于是GPIOx_CRL用于配置GPIO0-GPIO7的输入输出模式。同理GPIOx_CRH则用于配置GPIO8-GPIO15的输入输出模式。
控制PX0---PX7引脚工作模式
四个位控制一个引脚
2. 端口配置高寄存器(GPIOx_CRH)
控制PX8---PX15引脚工作模式
3.端口输入数据寄存器(GPIOx_IDR)
4. 端口输出数据寄存器(GPIOx_ODR)
5. 端口位设置/复位寄存器(GPIOx_BSRR)
如果想要将bit7和bit6置为0,则表示要将bit7和bit6写为1,其他位置为0
对复位上写1表示复位
对置位上写1表示置为
如果置位和复位同时设置,则置位起作用。
6.端口位复位寄存器(GPIOx_BRR)
7.端口配置锁定寄存器(GPIOx_LCKR)
8.复用功能I/O和调试配置(AFIO)
二、原理图分析
1.硬件接线
1)杜邦线连接到P0端口到LCD的J19接口,这样相当于8个LED分别对应PB8-PB15
2)因为GPIO接到LCD的负极,所以输入0亮,输入1暗
三、MDK工程建立
与前面51建立工程是一样的
1.注意点1:
2.注意点2:
单片机中用到的c语言其实不是原始c语言,而是有点定制性c语言
之前认识:整个程序从main函数开始执行,main执行完整个程序就结束
起始代码:从CPU复位开始执行的第一句指令,到main函数之前所做的事情就是起始代码
3.起始代码
1)起始代码是从哪里来的:编译环境提高的,STM32CPU提供的
2)不同CPU的起始代码一般是不同的
3)起始代码是用汇编写的
四、写代码控制GPIO点亮和熄灭
1.寄存器现象确认
我们使用的是P0端口(对应PB8-PB15),所以我们要去对应查找PB的寄存器起始地址
1)STM32 PortB的起始是0x4001 0c00
2.有可能使用到的GPIO的地址:
因为我们是PB8-PB15,所以我们只使用CRH
3.C语言操作寄存器
1)ARM是内存与IO统一编址的,所以ARM中的所有外设都是通过寄存器的方式来操作的
2)每一个寄存器都有地址,c语言通过这些地址来操作这些寄存器位,用到的c语言的技巧主要是c与的位操作和c语言指针
3)常见面试题:用c语言向内存地址0x3000 0004写入16
*(unsigned int *)0x3000 0004=16;
等价于
unsigned int *p=(unsigned int *)0x3000 0004;
*p=16;
4.解析寄存器
1.端口配置高寄存器(GPIOx_CRH)-高8位
//向CRH寄存器写内容,将GPIO_GPB15配置为输出模式【此时高位有用】
//推挽输出模式,输出的速率为50MHZ【0011 0011 0011 0011.。。】
*((unsigned int *)GPIOB_CRH)=0x33333333;//表示将GPIO_CRH的值设置为0x33333333
2.端口输出数据寄存器(GPIOx_ODR)
//向CRH寄存器写内容,将GPIO_GPB15配置为输出模式【此时高位有用】
//推挽输出模式,输出的速率为50MHZ【0011 0011 0011 0011.。。】
*((unsigned int *)GPIOB_CRH)=0x33333333;//表示将GPIO_CRH的值设置为0x33333333
5.注意点:
记得在操作GPIO的时候要先打开时钟
#define GPIOB_CRH 0x40010C04
#define GPIOB_ODR 0x40010C0C
#define RCC_APB2ENR 0x40021018
void main(void){
//STM32所有外设默认关闭
//所以我们在打开时钟
*((unsigned int *)RCC_APB2ENR)=0x00000008;
//向CRH寄存器写内容,将GPIO_GPB15配置为输出模式
//推挽输出模式,输出的速率为50MHZ【0011 0011 0011 0011.。。】
*((unsigned int *)GPIOB_CRH)=0x33333333;//表示将GPIO_CRH的值设置为0x33333333
//输出1,点亮LED
//ff:对应15-8位
*((unsigned int *)GPIOB_ODR)=0x0000f000;
while(1);
}
五、其他寄存器的测试
1.端口位设置/清除寄存器(GPIOx_BSRR)
将bit8-bit15置0(变亮),因为我们前面是使用了GPIO_GRH(所以只能控制8-15个引脚),所以此时改变0-7是无效的
//使用寄存器【GPIOB_BSRR】:将bit8-bit15置0(此时表示全亮)
//*((unsigned int *)GPIOB_BSRR)=0x00000000;
//因为前面我们是控制GPIOB_CRH,所以只有8-15位才起效果,0-7无论设置什么都无所谓
//*((unsigned int *)GPIOB_BSRR)=0x000000ff;//此时全亮,与上面效果一样
//0101 0101
//*((unsigned int *)GPIOB_BSRR)=0x00005500;//此时2,4,6,8亮
//此时led1,led3不亮,其他亮
//使用置位
*((unsigned int *)GPIOB_BSRR)=0x00005500;//此时2,4,6,8亮
//使用复位:将高4位全部复位
*((unsigned int *)GPIOB_BSRR)=0xf0000000;
2.端口位清除寄存器(GPIOx_BRR)
只能操作bit0-bit15
BRR:只能用来复位,不能置位(所以只能将其置为0,而不能置为1)
//使用寄存器【GPIOB_BRR】:将bit8-bit15置0(此时表示全亮)
//GPIOB_BRR:只能进行复位(意思是只能将电平置为0)
//前面16-32是无效位
*((unsigned int *)GPIOB_BRR)=0x00000000;//全亮
*((unsigned int *)GPIOB_BRR)=0x0000ffff;//全亮
//*((unsigned int *)GPIOB_BRR)=0x0000ff00;//只亮高4位(led的4-7)
六、总结
对数据操作的三种方法:
ODR:可以进行任何操作,但是只能一次性操作32位,效率低,但是移植性高【其次考虑】
BSRR:原子操作,效率高;移植性差【优先考虑】
BRR:只能进行复位,不能置位,原子操作,效率高;移植性差
1.代码
#define GPIOB_CRH 0x40010C04
#define GPIOB_CRL 0x40010C00
#define GPIOB_ODR 0x40010C0C
#define GPIOB_BSRR 0x40010C10
#define GPIOB_BRR 0x40010C14
#define RCC_APB2ENR 0x40021018
void main(void){
//STM32所有外设默认关闭
//所以我们在打开时钟
*((unsigned int *)RCC_APB2ENR)=0x00000008;
//向CRH寄存器写内容,将GPIO_GPB15配置为输出模式【此时高位有用】
//推挽输出模式,输出的速率为50MHZ【0011 0011 0011 0011.。。】
*((unsigned int *)GPIOB_CRH)=0x33333333;//表示将GPIO_CRH的值设置为0x33333333
//输出0,点亮LED
//ff:对应15-8位
//*((unsigned int *)GPIOB_ODR)=0x00000000;
//使用寄存器【GPIOB_BSRR】:将bit8-bit15置0(此时表示全亮)
//*((unsigned int *)GPIOB_BSRR)=0x00000000;
//因为前面我们是控制GPIOB_CRH,所以只有8-15位才起效果,0-7无论设置什么都无所谓
//*((unsigned int *)GPIOB_BSRR)=0x000000ff;//此时全亮,与上面效果一样
//0101 0101
//*((unsigned int *)GPIOB_BSRR)=0x00005500;//此时2,4,6,8亮
//此时led1,led3不亮,其他亮
//使用置位
//*((unsigned int *)GPIOB_BSRR)=0x00005500;//此时2,4,6,8亮
//使用复位:将高4位全部复位
//*((unsigned int *)GPIOB_BSRR)=0xf0000000;
//使用寄存器【GPIOB_BRR】:将bit8-bit15置0(此时表示全亮)
//GPIOB_BRR:只能进行复位(意思是只能将电平置为0)
//前面16-32是无效位
*((unsigned int *)GPIOB_BRR)=0x00000000;//全亮
*((unsigned int *)GPIOB_BRR)=0x0000ffff;//全亮
//*((unsigned int *)GPIOB_BRR)=0x0000ff00;//只亮高4位(led的4-7)
while(1);
}
2.对宏定义的封装
#define GPIOB_CRH 0x40010C04
#define GPIOB_CRL 0x40010C00
#define GPIOB_ODR 0x40010C0C
#define GPIOB_BSRR 0x40010C10
#define GPIOB_BRR 0x40010C14
#define RCC_APB2ENR 0x40021018
#define rGPIOB_CRH (*((unsigned int *)GPIOB_CRH))
#define rGPIOB_ODR (*((unsigned int *)GPIOB_ODR))
#define rGPIOB_BSRR (*((unsigned int *)GPIOB_BSRR))
#define rGPIOB_BRR (*((unsigned int *)GPIOB_BRR))
#define rRCC_APB2ENR (*((unsigned int *)RCC_APB2ENR))
void main(void){
rRCC_APB2ENR = 0x00000008;
rGPIOB_CRH = 0x33333333;
rGPIOB_ODR = 0x0000aa00;
}