系列文章目录
第一章 GPIO及寄存器详解
文章目录
前言
嵌入式期末复习
一、STM32F103ZET6芯片资源解读
内核:
32位 高性能ARM Cortex-M3处理器
时钟:高达72M,实际还可以超频一点点
单周期乘法和硬件除法
IO口:
144个引脚,112个IO口
大部分IO口都耐5V(模拟通道除外)
支持调试:SWD和JTAG,SWD只要2根数据线
存储器容量:
512K FLASH,64K SRAM
时钟,复位和电源管理:
2.0~3.6V电源和IO电压
上电复位,掉电复位和可编程的电压监控
强大的时钟系统
4~16M的外部高速晶振
内部8MHz的高速RC振荡器
内部40KHz低速RC振荡器,看门狗时钟
内部锁相环(PLL,倍频),一般系统时钟都是外部或者内部高速时钟经过PLL倍频后得到
外部低速32.768K的晶振,主要做RTC时钟源
低功耗:
睡眠,停止和待机三种低功耗模式
可用电池为RTC和备份寄存器供电
AD:
3个12位AD【多达21个外部测量通道】
转换范围:0~3.6(电源电压)
内部通道可以用于内部温度测量
内置参考电压
DA:
2个12位DA
DMA:
12个DMA通道(7+5=12; 7通道DMA1,5通道DMA2)
支持外设:定时器,ADC,DAC,SDIO,I2S,SPI,I2C,和USART
定时器,多达11个定时器:
4个通用定时器
2个基本定时器
2个高级定时器
1个系统定时器
2个看门狗定时器
通信接口,13个通信接口:
2个I2C接口
5个串口
3个SPI接口
1个CAN2.0
1个USB FS
1个SDIO
二、8种模式
1.输入
1,输入浮空
2,输入上拉
3,输入下拉
4,模拟输入
2.输出
1,开漏输出
2,推挽输出
3,推挽式复用功能
4,开漏式复用功能
3.stm32的IO口位配置表
输出模式配置表
三、3种速度
1,最大输出速度10MHz
2,最大输出速度2MHz
3,最大输出速度50MHz
四、每个IO端口有七个寄存器控制
1,CRL
2,CRH
配置模式的2 个 32 位的端口配置寄存器 CRL 和 CRH,控制着每个IO口的模式及输出速率
3,IDR
4,ODR
2 个 32 位的数据寄存器 IDR 和 ODR
5,BSRR
1 个 32 位的置位/复位寄存器BSRR
6,BRR
一个 16 位的复位寄存器 BRR
7,LCKR
1 个 32 位的锁存寄存器 LCKR
五、寄存器详细配置
1.CRL(配置低八位)
(1).模式配置图
(2).寄存器配置速记
0:模拟输入
4:浮空输入
8:上拉/下拉输入
C:保留
输入
1:推挽输出,速度10MHz
5:开漏输出,速度10MHz
9:复用功能推挽输出,速度10MHz
D:复用功能开漏输出,速度10MHz
速度10MHz 输出
2:推挽输出,速度2MHz
6:开漏输出,速度2MHz
A:复用功能推挽输出,速度2MHz
E:复用功能开漏输出,速度2MHz
速度2MHz 输出
3:推挽输出,速度50MHz
7:开漏输出,速度50MHz
B:复用功能推挽输出,速度50MHz
F:复用功能开漏输出,速度50MHz
速度50MHz 输出
2.CRH(配置高八位)
和CRL完全一样,只不过只是 CRL 控制的是低8 位输出口,而 CRH 控制的是高 8位输出口
3.IDR(端口输入数据寄存器,只读)
要想知道某个 IO 口的状态,你只要读这个寄存器,再看某个位的状态就可以了。
可以用来读取按键事件的发生,这里若PA0按下IDR的值为1。
int main()
{
Stm32_Clock_Init(9);
LED_Init();
RCC->APB2ENR|=1<<2;
GPIOA->CRL&=0xfffffff0;
GPIOA->CRL|=0x00000008;
while(1)
{
if(GPIOA->IDR==1)
{
Delay_ms(20);
LED0=!LED0;
Delay_ms(20);
}
}
}
也可以用来判断某些开关的状态,这里用来判断key0和key2是否连通。
int main()
{
Stm32_Clock_Init(9);
LED_Init();
RCC->APB2ENR|=1<<2;
GPIOA->CRL&=0xfffffff0;
GPIOA->CRL|=0x00000008;
while(1)
{
if(GPIOA->IDR==5)
{
LED0=!LED0;
Delay_ms(70);
}
}
}
4.ODR(端口输出数据寄存器,可读可写)
该寄存器为可读写,从该寄存器读出来的数据可以用于判断当前 IO 口的输出状态。而向该寄存器写数据,则可以控制某个 IO 口的输出电平。
ODR的读取和IDR的读取是有很大的区别的
ODR可读的意义在于:你在编程的时候,如果你需要通过该端口的当前电平来判断你的下一步操作,你就需要把它的电平放入if-else的判断语句或者switch块中。这时,虽然它的电平完全是由你的上一步操作决定,但如果我们可以直接从底层硬件中读回,那会给编程带来很大方便。
IDR寄存器的值是从输入端口的电平采样得到的(个人理解),直接反映芯片外部的情况。ODR的值反映的是你对输出端口电平的配置。两者完全不同
感谢知乎罗华昱的解答
int main()
{
Stm32_Clock_Init(9);
LED_Init();
RCC->APB2ENR|=1<<2;
GPIOA->CRL&=0xfffffff0;
GPIOA->CRL|=0x00000008;
GPIOA->ODR|=1<<2;
while(1)
{
if(GPIOA->ODR==4)
{
LED0=!LED0;
Delay_ms(70);
}
}
}
ODR寄存器写入数据
int main()
{
Stm32_Clock_Init(9);
RCC->APB2ENR|=1<<3;
GPIOB->CRL&=0xff00ffff;
GPIOB->CRL|=0x00330000;
while(1)
{
GPIOB->ODR=1<<5;
Delay_ms(60);
GPIOB->ODR=0<<5;
Delay_ms(60);
}
}
5.BSRR(端口位设置/清除寄存器)
在ODR里面提到过,BSRR可以分别地对各个ODR位进行独立的设置/清除
这怎么理解呢,就是字面意思
(1)ODR设置位值时是影响其他的位,ODR使用时先读取其状态才能设置其值。
(2)BSRR寄存器设置位值的时候,不会影响到其他的位的输出或输入。
为什么要这样呢
(1)用BSRR和BRR去改变管脚状态的时候,没有被中断打断的风险。也就不需要关闭中断。
(2)关闭中断明显会延迟或丢失一事件的捕获,所以控制GPIO的状态最好还是用SBRR和BRR。
int main()
{
Stm32_Clock_Init(9);
RCC->APB2ENR|=1<<3;
GPIOB->CRL&=0xff00ffff;
GPIOB->CRL|=0x00330000;
GPIOB->ODR=1<<4;
while(1)
{
Delay_ms(300);
GPIOB->ODR=1<<5;
Delay_ms(60);
GPIOB->ODR=0<<5;
Delay_ms(60);
Delay_ms(300);
}
}
可以看出在初始状态时D2是处于熄灭的状态,D1是点亮的状态,就是说此时ODR=00010000
而进入while(1)循环之后,虽然只修改了ODR的第五位,但是第四位还是一起被改变了。
怎么解决呢
int main()
{
Stm32_Clock_Init(9);
RCC->APB2ENR|=1<<3;
GPIOB->CRL&=0xff00ffff;
GPIOB->CRL|=0x00330000;
GPIOB->ODR=1<<4;
while(1)
{
Delay_ms(200);
GPIOB->BSRR=1<<5;
Delay_ms(60);
GPIOB->BSRR=1<<(5+16);
Delay_ms(60);
}
}
这里用BSRR寄存器进行设置,可以独立的修改位电平,这样D2就不受影响了。
6.BRR(端口位清除寄存器)
BRR只能将ODR的位清除,清除功能和BSRR差不多
int main()
{
Stm32_Clock_Init(9);
RCC->APB2ENR|=1<<3;
GPIOB->CRL&=0xff00ffff;
GPIOB->CRL|=0x00330000;
GPIOB->ODR=1<<4;
while(1)
{
Delay_ms(200);
GPIOB->BSRR=1<<5;
Delay_ms(60);
GPIOB->BRR=1<<5;
Delay_ms(60);
}
}
7.LCKR(端口配置锁定寄存器)
当执行正确的写序列设置了位16(LCKK)时,该寄存器用来锁定端口位的配置。位[15:0]用于锁定GPIO端口的配置。在规定的写入操作期间,不能改变LCKP[15:0]。当对相应的端口位执行了LOCK序列后,在下次系统复位之前将不能再更改端口位的配置。
每个锁定位锁定控制寄存器(CRL, CRH)中相应的4个位。