完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1. GPIO相关寄存器 对于每个GPI/O端口的寄存器:(32位)
1.1 配置寄存器(CRL、CRH) 2个32位的配置寄存器分别为配置低寄存器(GPIOx_CRL) 和 配置高寄存器(GPIOx_CRH) ① 端口配置低寄存器(GPIOx_CRL) (x=A…E) 结合下图来说明,低位寄存器是用来配置GPIO0到GPIO7的,A~E共5组GPIO,相对应的可以知道高位寄存器则用来配置A ~ E组寄存器的GPIO8到GPIO15;可以发现,每一个GPIO引脚由4个比特位控制,其中第2位是MODE,用来配置该GPIO是输入还是输出模式,如果是输出,对应引脚输出的最大速度;其中的高2位是CNF,用来配置引脚输入/输出时的具体功能模式,不同应用需求对应不同的模式设置(后文解释) ② 端口配置高寄存器(GPIOx_CRH) (x=A…E) 同上面的低位配置寄存器一样,只不过高位配置寄存器是用来配置GPIO8~GPIO15的;要知道,一般配置一个GPIO的输入输出模式时,先配置MODE,确定引脚是用来输入还是输出,其次再配置CNF来设置具体的工作模式 1.2 数据寄存器(IDR、ODR) 2个32位的数据寄存器分别是输入数据寄存器(GPIOx_IDR) 和 输出数据寄存器(GPIOx_ODR) ① 输入数据寄存器(GPIOx_IDR) (x=A…E) 1、输入数据寄存器是32位的,但是只用到0~15位,分别对应一组GPIO的16个引脚,仅可读; 2、0~15位组成一“字”,名为IDR,读的时候只能以字的形式读取,即一次读取16位的比特位; ② 输出数据寄存器(GPIOx_ODR) (x=A…E) 1、输出数据寄存器也是32位,只用到0~15位,分别对应一组GPIO的16个引脚,可读写; 2、读写寄存器时也只能以字的形式操作 1.3 位设置/清除寄存器(BSRR) 这是一个32位的寄存器,低16位(BS)用来设置端口的位,高16位(BR)则是用来清除端口的位;两者均仅可写,写操作时需以字的形式写入 如对于置位GPIO15时,令BS15为1表示置位,此时BR15就不能设为1了,如果BS为1时BR也为1,则默认BS生效 1.4 锁定寄存器(LCKR) 锁定寄存器(GPIOx_LCKR) (x=A…E)是一个32位的寄存器,当设置了位16(LCKK)时,则会锁定端口位的配置,位[15:0]对应的GPIO的16个引脚,当对相应的端口位执行了LOCK后,在下次系统复位之前将不能再更改端口位的配置 每个锁定位锁定控制寄存器(CRL, CRH)中相应的4个位,需要注意锁键的输入序列,顺序不要搞错,并且位[15:0]只能在LCKK为0时才可写 1.5 位清除寄存器(BRR) 位清除寄存器(GPIOx_BRR) (x=A…E) 的高16位保留,不使用;低16位以字的形式进行写入,写入1时才会对位有影响 2. ODR, BSRR, BRR控制位的区别
2、ODR改变时则是改变全部位的状态(仅可以字形式写) //对于ODR操作GPIO的伪代码如下:可见当控制GPIO的状态时最好不要使用ODR,关闭中断明显会延迟或丢失一事件的捕获 3、BSRR的高16位和BRR的低16位具有相同功能,举例: 对GPIOE的16个IO,改变低8位的数据而保持高8位不变4、有些IO时序要求非常严格,同时对一个GPIO置1和对另一个GPIO清0是很必要的 5、BSRR还有一个特点,就是Set比Reset的级别高 3. GPIO口的几种模式总结 3.1 常见名词解释 内置外设:如串口、ADC、DCA等等(属于stm32内部功能模块) 端口复用:当一个GPIO作为内置外设的功能引脚时,称为复用。比如说,STM32的串口1的引脚对应的I/O位PA9、PA10。而PA9、PA10默认的功能都是GPIO,所以说当PA9、PA10引脚作为串口1使用的时候就是端口复用 端口重映射:把引脚的内置外设功能映射到其他的引脚上,但不是可以随便映射的,需要参照数据手册 推挽结构:两个参数相同的三极管或MOS管分别受两互补信号的控制,总是在一个三极管或MOS管导通的时候另一个截止。高低电平由输出电平决定。有推有拉,任何时候IO口的电平都是确定的,不需要外接上拉或者下拉电阻 开漏输出:只可以输出强低电平,高电平得靠外部电阻拉高。输出端相当于三极管的集电极。适合于做电流型的驱动,其吸收电流的能力相对强(一般20ma以内); GPIO内部电路结构: FT:代表GPIO口兼容3.3V和5V 保护二极管:IO引脚上下两边两个二极管用于防止引脚外部过高、过低的电压输入 P-MOS、N-MOS管:由P-MOS管和N-MOS管组成的单元电路使得GPIO具有“推挽输出”和“开漏输出”的模式 TTL肖特基触发器:信号经过触发器后,模拟信号转化为0和1的数字信号 GPIO支持4种输入模式,三种最大翻转速度(2MHz、10MHz、50MHz) 3.2 四种输入模式 ① 浮空输入(GPIO_Mode_IN_FLOATING) 逻辑器件与引脚即不接高电平,也不接低电平,相当于浮在空中呈高阻态,上面用绳子一拉就上去了,下面用绳子一拉就沉下去了,一般用来做ADC输入用,这样可以减少上下拉电阻对结果的影响 ② 输入上拉(GPIO_Mode_IPU) 上拉就是通过上拉电阻把电位拉高,比如拉到Vcc,将输入的不确定的信号嵌位在高电平 ③ 输入下拉(GPIO_Mode_IPD) 下拉就是把电压拉低,拉到GND,下拉电阻另一端接地即可,原理与上拉的一样 ④ 模拟输入(GPIO_Mode_AIN) 模拟输入是指0,1的二进制数字信号,通过数模转换,转换成模拟信号,模拟输入模式下,I/O端口的模拟信号(电压信号,而非电平信号)直接模拟输入到片上外设模块,比如ADC模块等等 3.3 四种输出模式 ① 开漏输出(GPIO_Mode_Out_OD) 输出端相当于三极管的集电极,c极是开路的,可以接一个电阻到3.3V,也可以接一个电阻到5V,这样,在输出1的时候,就可以是5V电压,也可以是3.3V电压 开漏输出模式下,通过设置位设置/清除寄存器或者输出数据寄存器的值,途经N-MOS管,最终输出到I/O端口 这里要注意N-MOS管,当设置输出的值为高电平的时候,N-MOS管处于关闭状态,此时I/O端口的电平就不会由输出的高低电平决定,而是由I/O端口外部的上拉或者下拉决定;同时,I/O端口的电平也可以通过输入电路进行读取;但是I/O端口的电平不一定是输出的电平 当设置输出的值为低电平的时候,N-MOS管处于开启状态,此时I/O端口的电平就是低电平 输出端相当于三极管的集电极,要得到高电平状态需要上拉电阻才行,适合于做电流型的驱动,其吸收电流的能力相对强(一般20mA以内)② 开漏复用功能(GPIO_Mode_AF_OD) 开漏复用输出模式,与开漏输出模式很是类似。只是输出的高低电平的来源,不是让CPU直接写输出数据寄存器,取而代之利用片上外设模块的复用功能输出来决定的 ③ 推挽式输出(GPIO_Mode_Out_PP) 可以输出高,低电平,连接数字器件;推挽结构一般是指两个三级管分别受到互补信号的控制,总是在一个三极管导通的时候另一个截止。高低电平由IC的电源低定。同时,I/O端口的电平也可以通过输入电路进行读取;注意,此时I/O端口的电平一定是输出的电平 1、两只对称的功率开关管每次只有一个导通,所以导通损耗小,效率高 2、可以向负载灌电流 3、提高电路的负载能力,又提高开关速度 ④ 推挽式复用功能(GPIO_Mode_AF_PP) 推挽复用输出模式,与推挽输出模式很是类似。只是输出的高低电平的来源,不是让CPU直接写输出数据寄存器,取而代之利用片上外设模块的复用功能输出来决定的 小结 通常有5种方式使用某个引脚功能,它们的配置方式如下: 1)作为普通GPIO输入:根据需要配置该引脚为浮空输入、带弱上拉输入或带弱下拉输入,同时不要使能该引脚对应的所有复用功能模块。 2)作为普通GPIO输出:根据需要配置该引脚为推挽输出或开漏输出,同时不要使能该引脚对应的所有复用功能模块。 3)作为普通模拟输入:配置该引脚为模拟输入模式,同时不要使能该引脚对应的所有复用功能模块。 4)作为内置外设的输入:根据需要配置该引脚为浮空输入、带弱上拉输入或带弱下拉输入,同时使能该引脚对应的某个复用功能模块。 5)作为内置外设的输出:根据需要配置该引脚为复用推挽输出或复用开漏输出,同时使能该引脚对应的所有复用功能模块。 3.4 常用内置外设端口模式 端口位配置表 输出模式位 其他 4. C语言位操作复习 1字(word)= 2字节(byte) 1字节(byte) = 8位(bit) 32位计算机的CPU一次最多能处理32位数据,即4字节 64位计算机的CPU一次最多能处理64位数据,即8字节 [tr]符号描述运算规则[/tr]
以下代码演示环境:gcc + 64位系统 ① 获取一字节数据: #include void HextoTwo(int num) //递归,16进制转2进制 { int remainder; char buf[16][5] = {"0000","0001","0010","0011", "0100","0101","0110","0111", "1000","1001","1010","1011", "1100","1101","1110","1111"}; if(0 == num) return; remainder = num % 16; HextoTwo(num >> 4); printf("%s ", buf[remainder]); } void main() { unsigned int a = 0x12345678; //32bit unsigned int b = 0x000000ff; printf("n原数据为:%xn",a); printf("unsigned int类型的大小:%ld 字节n",sizeof(a)); printf("a的二进制数为:");HextoTwo(a);printf("n"); printf("b的二进制数为:");HextoTwo(b);printf("nnn"); printf("0 bit a >> 0 :");HextoTwo((a>>0));printf("n"); printf("8 bit a >> 8 :");HextoTwo((a>>8));printf("n"); printf("16bit a >> 16 :");HextoTwo((a>>16));printf("n"); printf("24bit a >> 24 :");HextoTwo((a>>24));printf("nnn"); printf("第一个字节:");HextoTwo((a>>0)&b);printf("tt%x",(a>>0)&b);printf("n"); printf("第二个字节:");HextoTwo((a>>8)&b);printf("tt%x",(a>>8)&b);printf("n"); printf("第三个字节:");HextoTwo((a>>16)&b);printf("tt%x",(a>>16)&b);printf("n"); printf("第四个字节:");HextoTwo((a>>24)&b);printf("tt%x",(a>>24)&b);printf("nnn"); } 运行结果如下图,对0x12345678(共32bit,4字节)操作,在黄色框里可以看到每右移增加8个bit(相当于右移1个字节),二进制数据段变少,其实是高位补0没有显示出来;然后将移位后的数据与0x000000ff相与,实际上是只有低8位能够保留,得到的自然就是最后一个字节了 ② 获取某一位: 使用宏定义,比如获取0x68的底3位,先让1左移3,得到1000,1000与01101000相与,得到1000,1000右移3得到1,即第3位为1 #include #define GET_BIT(x, bit) ((x & (1 << bit)) >> bit) /* 获取第bit位 */ void main() { unsigned int a = 0x68; /*二进制为:0110 1000*/ printf("nn"); printf("0x68的第0位:%dn",GET_BIT(a,0)); printf("0x68的第1位:%dn",GET_BIT(a,1)); printf("0x68的第2位:%dn",GET_BIT(a,2)); printf("0x68的第3位:%dn",GET_BIT(a,3)); printf("0x68的第4位:%dn",GET_BIT(a,4)); printf("0x68的第5位:%dn",GET_BIT(a,5)); printf("0x68的第6位:%dn",GET_BIT(a,6)); printf("0x68的第7位:%dnnn",GET_BIT(a,7)); } ③ 对32bit数据的字节清零、置1操作: #include #define CLEAR_LOW_BYTE0(x) (x &= 0xffffff00) /* 清零第0个字节 */ #define CLEAR_LOW_BYTE3(x) (x &= 0xff00ffff) /* 清零第3个字节 */ #define SET_LOW_BYTE0(x) (x |= 0x000000ff) /* 第0个字节置1 */ #define SET_LOW_BYTE3(x) (x |= 0xff000000) /* 第3个字节置1 */ void main() { unsigned int a = 0x11111111; unsigned int b = 0x11111111; unsigned int c = 0x22222222; unsigned int d = 0x22222222; printf("nn"); printf(" 原数据:%xn",a); printf("清零第0个字节后:%xn",CLEAR_LOW_BYTE0(a)); printf("清零第3个字节后:%xnnn",CLEAR_LOW_BYTE3(b)); printf(" 原数据:%xn",c); printf("置位第0个字节后:%xn",SET_LOW_BYTE0(c)); printf("置位第3个字节后:%xn",SET_LOW_BYTE3(d)); } 5. 配置GPIO的一般步骤 配置一个初始化led端口的函数: #include "led.h" //初始化 PA8为输出口.并使能的时钟 //LED IO 初始化 void LED_Init(void) { RCC->APB2ENR |= 1<<2; //使能 PORTA 时钟 GPIOA->CRH &= 0XFFFFFFF0; GPIOA->CRH |= 0X00000003;//PA8 推挽输出 GPIOA->ODR |= 1<<8; //PA8 输出高 } 由于还没有写到时钟相关的寄存器,这里先不看时钟;先对GPIOA的CRH寄存器的低4位进行清0操作,CRH寄存器是端口配置高寄存器,控制8-15的io口的配置,则完成了对PA8相关配置寄存器的清零操作 接着将PA8端口的配置寄存器对应位设为0x3,即0011,00表示通用推挽输出,11表示输出模式,最大速度50MHz,这就完成了基础的配置设置 最后再通过ODR寄存器,对第8位(即PA8端口)写1,代表初始化后该端口点平为高 |
||
|
||
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1777 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1621 浏览 1 评论
1080 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
728 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1678 浏览 2 评论
1938浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
731浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
570浏览 3评论
595浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
554浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-23 08:46 , Processed in 0.872466 second(s), Total 78, Slave 62 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号