STM3216位IO口操作的一些教训,STM32操作IO口的寄存器是16位,但是高低8位分别并口操作不同的器件,怎么办,会覆盖数据。BSRR 设计的目的就是为了能同时操作想修改的位0不影响1或置1或0

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 的形式, 所以担心是多余的

http://cache.baiducontent.com/c?m=qEl4aapXn2unsSRr0UkEJg7BjnGtBloWXAH6FZr5c7TB3r5hi1h0i44UvCAeICzJ8cIXIQzu9QHwOlaBDW5G69Nku2dOnMykzGVt21x8k87NWaxucNAu6mE-tCJ9MA98KfiyRuZhRGPkP3LlGlruKKzoFbtLbK5NQwKuN23jl2M-zEAKJ3wb_N5Yzx-Flwi8D1Ol1tNzpAjoiZ2aIP73gK&p=882a9e40ce8819dd0be2962b1705&newp=aa6e8f16d9c152b50dbd9b75085e92695d0fc20e3cd7d601298ffe0cc4241a1a1a3aecbf2c261204d0c5786d04a84a57eaf33d763d0034f1f689df08d2ecce7e3c9272&s=6e01383fd96a17ae&user=baidu&fm=sc&query=gpio%5Fodr%C8%E7%BA%CE%B2%D9%D7%F78%CE%BB%B2%A2%BF%DA%2C&qid=c19e07670002c073&p1=14

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位进行的

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值