STM3216位IO口操作的一些教训
yuanmeixiang 2017-05-05 20:12:24 8783 收藏 9
分类专栏: STM32 文章标签: stm32 8位操作
版权
最近在用TFT屏的时候走啦不少弯路,因为TFT屏都是16位的,所以大部分的都是采用一组IO口对应16个数据位刚好并口传输数据,最近因为IO口紧缺,所以想要改成串口,但是发现串口刷新屏幕太慢所以最后改成8位的并口,但是STM32操作IO口的寄存器是16位的。也就是说用并口的话一起传输数据进去虽然只用8位但是操作还是16位一起操作,虽然有8位没用到但是还是在传输数据进去的时候被一起操作,会把不用的8位的值一起覆盖。
#define DATAOUT(x) GPIOB->ODR=x; //数据输出
如上当调用宏的时候整个B端口一起被操作。
方法1: ODR配置法,可以直接使用 GPIO->ODR = value << N;给对应的IO口进行输出操作。
但是此时在硬件上要保证:
1. 该功能口(如GPIOA)上的其余的IO口不作为普通的IO输出口使用
2. 行列之外的GPIO口,要么是普通的IO输入口,要么是功能口。功能口就是UART、USB、PWM、ADC等,对GPIO的ODR进行操作时,不会对功能口产生影响。
也就是说比如现在选择B端口的高8位,那么8--15IO口被选择用来驱动TFT,但是0-7IO口不能在用着普通输出IO口,但是可以设置为输入,或者UART,usb,PWM,ADC等功能。
方法2
可以用位操作的方式单独对IO口进行操作,但是这样操作数据刷新速度很慢。
void WriteData(u8 data)
{
//分别操作与液晶连接的IO口,顺序是由高位到低位
GPIO_WriteBit(GPIO号, Pin号,(data & 0x80) >> 7 );
GPIO_WriteBit(GPIO号, Pin号,(data & 0x40) >> 6 );
GPIO_WriteBit(GPIO号, Pin号,(data & 0x20) >> 5 );
GPIO_WriteBit(GPIO号, Pin号,(data & 0x10) >> 4 );
GPIO_WriteBit(GPIO号, Pin号,(data & 0x08) >> 3 ) ;
GPIO_WriteBit(GPIO号, Pin号,(data & 0x04) >> 2 );
GPIO_WriteBit(GPIO号, Pin号,(data & 0x02) >> 1 );
GPIO_WriteBit(GPIO号, Pin号,data & 0x01);
}
使用这种方式就不会影响到底8位,但是这样刷新速度很慢。
————————————————
版权声明:本文为CSDN博主「yuanmeixiang」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/yuanmeixiang/article/details/71239664
c、然后就是对GPIO初始化,操作。操作方式见如下两种
对整个GPIO口操作,需要用GPIOx_ODR寄存器,这个寄存器只能按16位操作。如果需要对某个位操作。则用到 IO 位段映射。下面简述两种方法
a、对GPIOx_ODR操作:
//PB0~7,作为数据线
#define DATAOUT(x ) GPIOB->ODR=(GPIOB->ODR&0xff00)|(x&0x00FF); //输出 这样子操作不影响ODR的高8位。
但是这样如果高8位也是输出的话,输出口会直接输出1,会影响高8位所连接的器件2021.5.12
b、位段映射方法:将IO口每一位映射到ram空间中去。
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
BSRR 就是用来进行位操作的,想操作几位都行,设计的目的就是为了能同时操作想修改的位而其他位不变,好好看资料吧。
但是这些位也是只能以16位形式进行操作
STM32中单独设置GPIO端口高8位/低8位的方法
畅学单片机 2019-03-21 2177浏览 0评论 1点赞
stm32的IO端口都是16位的, 如果要单独操作某高8位或低8位, 则不是那么简单, 先看两张BSRR/BRR寄存器的图:
据官方数据手册上面说, 这两个寄存器用于专门对ODR进行原子操作的位操作, 都是在置1的时候对某位有影响.
举例说下怎么对IO端口赋值:
1.对高8位/低8位/全部清零
很明显, 这个只需要操作BRR寄存器即可:
对高8位清零:GPIOA->BRR = 0xFF00
对低8位清零:GPIOA->BRR = 0x00FF
全部清零: GPIOA->BRR = 0xFFFF 或 GPIOA->ODR = 0x0000
当然了, 使用下面2,3的两个宏也可以完全该清零操作~ stm32固件库是不是应该加上这两个宏/函数?
2.对低8位置数
涉及到置数, 这个就是操作BSRR寄存器了
比如要使端口A的低8位为 0x55 (01010101B), 那么对于BSRR这个32位寄存器来说:
低16位应该置为 0000 0000 0101 0101, 这个就等于 0x55, 置1使某位为1, 置0的位不影响原来的值
高16位应该置为 0000 0000 1010 1010, 这个就等于 ~0x55(即取反)的结果, 置1使某位为0, 置0不影响原来的值
这样, BSRR寄存器的值就是 0000 0000 1010 1010 0000 0000 0101 0101, 两部分的高8位均为0, 所以不会影响到IO口的高8位
总结, 以下的宏实现对某端口的低8位置数, 不影响高8位:
#define GPIO_WriteLow(GPIOx,a) GPIOx->BSRR=(((uint32_t)(uint8_t)~(a))<<16)|((uint32_t)(uint8_t)(a))
3.对高8位置数
这个和单独对低8位置数其实是一样的, 只是设置的位不一样罢了
同样, 要使高8位为0x55, 那么:
低16位应该置为 0101 0101 0000 0000
高16位应该置为 1010 1010 0000 0000, 同样是取反的结果; 不影响低8位的数据
这样, BSRR寄存器的值就是 1010 1010 0000 0000 0101 0101 0000 0000, 可以看出, 其实它就是上面那个结果左移8位
总结, 以下的宏实现对某端口的高8位置数, 不影响低8位:
#define GPIO_WriteHigh(GPIOx,a) GPIOx->BSRR=(((uint8_t)(uint8_t)~(a))<<24)|(((uint32_t)(uint8_t)(a))<<8)
大家不用担心效率问题, 上面那两个宏最终的结果就是 GPIOx->BSRR=value 的形式, 所以担心是多余的
GPIO 配置之ODR, BSRR, BRR 详解
prayer521 2016-09-07 原文
STM32 GPIO 配置之ODR, BSRR, BRR 详解
用stm32 的配置GPIO 来控制LED 显示状态,可用ODR,BSRR,BRR 直接来控制引脚输出状态.
ODR寄存器可读可写:既能控制管脚为高电平,也能控制管脚为低电平。
管脚对于位写1 gpio 管脚为高电平,写 0 为低电平
BSRR 只写寄存器:[color=Red]既能控制管脚为高电平,也能控制管脚为低电平。
对寄存器高 16bit 写1 对应管脚为低电平,对寄存器低16bit写1对应管脚为高电平。写 0 ,无动作
BRR 只写寄存器:只能改变管脚状态为低电平,对寄存器 管脚对于位写 1 相应管脚会为低电平。写 0 无动作。
刚开始或许你跟我一样有以下疑惑:
1.既然ODR 能控制管脚高低电平为什么还需要BSRR和BRR寄存器?
2.既然BSRR能实现BRR的全部功能,为什么还需要BRR寄存器?
对于问题 1 ------ 意法半导体给的答案是---
“This way, there is no risk that an IRQ occurs between the read and the modify access.”
什么意思呢?就就是你用BSRR和BRR去改变管脚状态的时候,没有被中断打断的风险。也就不需要关闭中断。
用ODR操作GPIO的伪代码如下:
disable_irq()
save_gpio_pin_sate = read_gpio_pin_state();
save_gpio_pin_sate = xxxx;
chang_gpio_pin_state(save_gpio_pin_sate);
enable_irq();
关闭中断明显会延迟或丢失一事件的捕获,所以控制GPIO的状态最好还是用SBRR和BRR
对于问题 2 ------- 个人经验判断意法半导体仅仅是为了程序员操作方便估计做么做的。
因为BSRR的 低 16bsts 恰好是set操作,而高16bit是 reset 操作 而BRR 低 16bits 是reset 操作。
简单地说GPIOx_BSRR的高16位称作清除寄存器,而GPIOx_BSRR的低16位称作设置寄存器。
另一个寄存器GPIOx_BRR只有低16位有效,与GPIOx_BSRR的高16位具有相同功能。
举个例子说明如何使用这两个寄存器和所体现的优势。
例如GPIOE的16个IO都被设置成输出,而每次操作仅需要
改变低8位的数据而保持高8位不变,假设新的8位数据在变量Newdata中,
这个要求可以通过操作这两个寄存器实现,STM32的固件库中有两个函数
GPIO_SetBits()和GPIO_ResetBits()使用了这两个寄存器操作端口。
上述要求可以这样实现:
GPIO_SetBits(GPIOE, Newdata & 0xff);
GPIO_ResetBits(GPIOE, (~Newdata & 0xff));
也可以直接操作这两个寄存器:
GPIOE->BSRR = Newdata & 0xff;
GPIOE->BRR = ~Newdata & 0xff;
当然还可以一次完成对8位的操作:
GPIOE->BSRR = (Newdata & 0xff) | ( (~Newdata & 0xff)<<16 );
当然还可以一次完成对16位的操作:
GPIOE->BSRR = (Newdata & 0xffff) | ( (~Newdata )<<16 );
从最后这个操作可以看出使用BSRR寄存器,可以实现8个端口位的同时修改操作。
有人问是否BSRR的高16位是多余的,请看下面这个例子:
假如你想在一个操作中对GPIOE的位7置'1',位6置'0',则使用BSRR非常方便:
GPIOE->BSRR = 0x400080; //有个疑问BSRR只能支持16位的操作,这24位BSRR能识别能写进去吗?2021.5.13可能是整个BSRR支持32位操作
如果没有BSRR的高16位,则要分2次操作,结果造成位7和位6的变化不同步!
GPIOE->BSRR = 0x80;
GPIOE->BRR = 0x40;
BSRR还有一个特点,就是Set比Reset的级别高,
就是说同一个bit又做Set又做Reset,最后结果是Set
要同步变化只要简单的 GPIOx->BSRR = 0xFFFF0000 | PATTEN;
即可,不用考虑哪些需要置1,哪些需要清零
从最后这个操作可以看出使用BSRR寄存器,可以实现8个端口位的同时修改操作。0不影响1影响可以高8位0低8位1.但是低8位数据如果是01010101呢?0是写不进去的,0对ODR没有影响,还需要操作一次BSRR的高16位,它是对ODR的各位置0,或者操作BRR也是对ODR各位置0。需要两次操作
但是BSRR只能进行16位操作,如何区分高低16位呢?可能是整个BSRR支持32位操作
http://www.cnblogs.com/shangdawei/p/4723941.html
GPIOx->BSRR = GPIO_Pin; //端口位设置或清除
注:GPIO_Pin为(0..15)中任意接口
有了GPIOx->BRR清除寄存器,并且与GPIOx->BSRR高16为功能相同,有人问是否BSRR的高16位是多余 的,请看下面的例子:
假如你想在一个操作中对GPIOE的位1置'1',位15置'0',则使用BSRR非常方便:
GPIOE->BSRR = 0x80000002;
低16位中的0002将位1置‘1’,高16位中的8000将位15置清零,一步就可以做到。
如果没有BSRR的高16位,则要分2次操作,结果造成位1和位15的变化不同步!
GPIOE->BSRR = 0x02;
GPIOE->BRR = 0x8000;
百度百科中GPIOx->BSRR
最近刚刚接触m3处理器,看到它的GPIO寄存器时,发现看到的好多资料上有一个不理解的地方。。。
“STM32的每个GPIO端口都有两个特别的寄存器,GPIOx_BSRR和GPIOx_BRR寄存器,通过这两个寄存器可以直接对对应的GPIOx端口置'1'或置'0'。
GPIOx_BSRR的高16位中每一位对应端口x的每个位,对高16位中的某位置'1'则端口x的对应位被清'0';寄存器中的位置'0',则对它对应的位不起作用。
GPIOx_BSRR的低16位中每一位也对应端口x的每个位,对低16位中的某位置'1'则它对应的端口位被置'1';寄存器中的位置'0',则对它对应的端口不起作用。
假如你想在一个操作中对GPIOE的位7置'1',位6置'0',则使用BSRR非常方便:GPIOE->BSRR = 0x4080”
既然BSRR高16位是复位,低16位是置位,那么为什么如上所述,复位第6位的时候不是用高16位中的第6位,而是用的低16位的高8位呢??
我的理解是应该把0X4080 改为0X0040 0080,才能实现位6复位,位7置位。。不知道我这样理解对不对,希望有知道的帮我解答。。。
谢谢
等了一天没有给回的。。。自己跑实验室去找到板子试了试,得出结果是我的怀疑是正确的。。。
也就是说BSRR是32位的,高16位用于复位,低16位用于置位。所以如果想要“GPIOE的位7置'1',位6置'0'”则要GPIOE->BSRR = 0x4000 0080(应该是0X0040 0080原文写错了意思是对的也验证了BSRR支持32位操作2021.5.13),而不是GPIOE->BSRR = 0x4080。。。
http://bbs.eeworld.com.cn/thread-296176-1-1.html
教程中描述表示BSRR支持32位操作虽然高低都是16位进行的