搜索
bottom↓
12
返回列表 发新帖
楼主: wuzhujian

MDK:位区(bitband)操作新方法

  [复制链接]

出0入0汤圆

发表于 2012-12-24 13:14:52 | 显示全部楼层
大神:
以下是MDK自带文档:
Using __attribute__((bitband))
RealView Compilation Tools for µVision Compiler User Guide Version 4.0

Home > Compiler Features > Bit-banding > Using __attribute__((bitband))

3.3.1. Using __attribute__((bitband))
__attribute__((bitband)) is a type attribute that is used to bit-band type definitions of structures. See Example 3.3 and Example 3.4.

Example 3.3. Unplaced object

/* foo.c */

typedef struct {
  int i : 1;
  int j : 2;
  int k : 3;
} BB __attribute__((bitband));

BB value; // Unplaced object

void update_value(void)
{
    value.i = 1;
    value.j = 0;
}

/* end of foo.c */



In Example 3.3 the unplaced bit-banded objects must be relocated into the bit-band region. You can do this by either using an appropriate scatter-loading description file or by using the --rw_base linker command-line option. See the Linker Reference Guide for more information.

Alternatively, you can use __attribute__((at())) to place bit-banded objects at a particular address in the bit-band region, see Example 3.4.

Example 3.4. Placed object

/* foo.c */

typedef struct {
  int i : 1;
  int j : 2;
  int k : 3;
} BB __attribute__((bitband));

BB value __attribute__((at(0x20000040))); // Placed object

void update_value(void)
{
    value.i = 1;
    value.j = 0;
}

/* end of foo.c */
我的理解是:
  对于内存RAM的变量也可进行位操作,而且也可以映射到位带里去。但必须用两种方法提示编译器(MDK)1: You can do this by either using an appropriate scatter-loading description file or by using the --rw_base linker command-line option.就是通过:连接脚本或连接参数 --rw_base 去设置。
2. you can use __attribute__((at())) to place bit-banded objects at a particular address in the bit-band region, 通过__attribute__((at())) 提示MDK,这样就可以操作内存数据区里的变量了。求高手指点。

出0入93汤圆

发表于 2012-12-24 13:41:15 | 显示全部楼层
xizi 发表于 2012-11-23 23:12
缺点是不能整体赋值。比如
typedef union
{

胡说。

以楼主第一楼的代码为例,改成:
  1. #pragma anon_unions                                                        //允许匿名结构和匿名联合

  2. typedef union {                                                                // 状态寄存器结构
  3.         struct {
  4.                 u16 PE:1;                                                        // 校验错误(r)
  5.                 u16 FE:1;                                                        // 帧错误(r)
  6.                 u16 NE:1;                                                        // 噪声错误标志(r)
  7.                 u16 ORE:1;                                                        // 过载错误(r)
  8.                 u16 IDLE:1;                                                // 监测到总线空闲(r)
  9.                 u16 RXNE:1;                                                // 读数据寄存器非空(rc_w0)
  10.                 u16 TC:1;                                                        // 发送完成(rc_w0)
  11.                 u16 TXE:1;                                                        // 发送数据寄存器空(r)
  12.                 u16 LBD:1;                                                        // LIN断开检测标志(rc_w0)
  13.                 u16 CTS:1;                                                        // CTS 标志(rc_w0)
  14.         } __attribute__((bitband));
  15.         u16 Value;
  16. } USART_SR;

  17. #define USART1_SR        (*(volatile USART_SR *)0x40013800)
复制代码
访问位成员:pe = USART1_SR.PE;
访问整体:sr = USART1_SR.Value;


还要怎样访问整体?只是比一般的多出了.Value了。千万注意,#pragma anon_unions不能缺少

出0入0汤圆

发表于 2012-12-24 20:02:26 | 显示全部楼层
wuzhujian 发表于 2011-2-24 23:21
STM32F10x的寄存器文件(修改过的)。
点击此处下载 ourdev_618284JGMW9Q.rar(文件大小:16K) (原文件名:stm ...

从参考手册上看所有外设的地址都是在0x4000 0000-0x4010 0000这段内存中,这段内存是可以映射到0x42000000-0x43ffffff这个位段里的。



ARM公司规划出这个外设的映射位段是为了加快位操作速度。对某一位进行写入时,取消了传统的 读 改 写 三步骤,而是可以应用这个位段映射,直接写入。
而ARM公司没有放弃传统的外设段,全部应用位段的理由也是显而易见的。如果全部用位段,想象一下,如果对整个寄存器单次全部赋值。用位段的方式,需要几个步骤,多少代码....32位的寄存器赋值,是否需要32次置位或者清零动作?或者32的倍数???


各有优点各有缺点......


完美的做法是,编译器智能识别,当为位操作时,就用位段操作。当为批量操作时,就用传统段。

出0入93汤圆

发表于 2012-12-25 09:41:34 | 显示全部楼层
Lavion 发表于 2012-12-24 20:02
从参考手册上看所有外设的地址都是在0x4000 0000-0x4010 0000这段内存中,这段内存是可以映射到0x4200000 ...

楼上,我在你的楼上提出的方案是可以兼顾的……只是写法比较长了一点。

出0入0汤圆

发表于 2012-12-25 09:55:48 | 显示全部楼层
takashiki 发表于 2012-12-24 13:41
胡说。

以楼主第一楼的代码为例,改成:

请你先验证一下我的代码,然后再下结论我是否在胡说。我的代码确实编译通不过,我才有此一说。我的代码哪里有问题,你要是能指出来,我会非常感谢。
与你的代码比较了一下,至少有两点不一样:

1. 你使用了#pragma anon_unions,我不知其作用,回头尝试一下。
2. 你定义的是USART1的特殊寄存器结构体,#define USART1_SR        (*(volatile USART_SR *)0x40013800);我的代码是打算定义一个普通程序标志结构体,mytype var。

我会仿照你的代码尝试一下,回头与你交流。

出0入0汤圆

发表于 2012-12-25 10:28:44 | 显示全部楼层
这个还不错哦。

出0入93汤圆

发表于 2012-12-25 11:16:44 | 显示全部楼层
xizi 发表于 2012-12-25 09:55
请你先验证一下我的代码,然后再下结论我是否在胡说。我的代码确实编译通不过,我才有此一说。我的代码哪 ...

首先请问一下,你是否认真看过我的代码并动手编译过?你的代码是有语法错误的,你拿来怪编译强不是在欺负人嘛!

这是你的代码:
  1. typedef union
  2. {
  3.     u8 byte;
  4.     struct
  5.     {
  6.         u8 bit0:1;
  7.         u8 bit1:1;
  8.     } bits;

  9. } mytype __attribute__((bitband));                          //看到没有,bitband把byte也作用进去了,这是不支持的。

  10. mytype var;                                                        //这里,你的变量var位于什么位置?编译器他不知道是不是可以通过位带来完成,于是就SB了

  11. var.byte = 0xff;
复制代码
我给你改写一下,你自己编译一下,再验证一下我并没有冤枉你:
  1. #pragma anon_unions
  2. typedef union{
  3.     u32 byte;
  4.     struct    {
  5.         u32 bit0:1;
  6.         u32 bit1:1;
  7.     } __attribute__((bitband));                               //bitband必须作用整体,这里必须匿名,否则bitband认为只作用于部分,编译能够通过,但是无法实现bitband的功能
  8.                                                                        //这里用了匿名结构,所以一定要#pragma anon_unions,否则语法错误伺候
  9. } mytype;              

  10. volatile mytype __var __attribute__((at(0x20001000)));               //占个座
  11. #define var (*(volatile mytype*)0x20001000)         //实际操作都用var来完成。
复制代码
在我的MDK上,编译器上还是有点傻,直接调用__var.bit0还是会移位干什么的,讨厌死了,但是var.bit0就不会。

有图有XX:


看到@0800017C和@08000180了没有,分别对应22020000和20001000,表示已经完全采用了bitband。
如果图再看不懂,那你还是坚持你自己的观点好了,我只是说Keil完全可以做到bitband访问、局部访问(通过逻辑运算)和全部访问,至于你能否做到,那已经不是编译器的事情了。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

出0入0汤圆

发表于 2012-12-25 23:05:47 | 显示全部楼层
takashiki 发表于 2012-12-25 11:16
首先请问一下,你是否认真看过我的代码并动手编译过?你的代码是有语法错误的,你拿来怪编译强不是在欺负 ...

仅仅适用与M3, M4。除了特殊应用,很少用到。
变量定义的时候,要注意对齐和边界
不过从技术角度讲,顶一下....

出0入0汤圆

发表于 2012-12-26 04:43:05 | 显示全部楼层
本帖最后由 xizi 于 2012-12-26 04:44 编辑
takashiki 发表于 2012-12-25 11:16 【严重警告:本论坛不得使用大尺寸字体。请重新编辑删除本警告。不删除此警告的ID将被封锁。阅读到此文字请务必举报】
首先请问一下,你是否认真看过我的代码并动手编译过?你的代码是有语法错误的,你拿来怪编译强不是在欺负 ...


我手头的计算机没有MDK,我只能到单位去试验。但是看了你对我的代码逐行点评,我知道了我错在哪里。我诚恳接受你的批评,我果然是在胡说。我只知道照猫画虎地搬来xdata区位结构体的整体访问方法,没想到bitband区位结构体的整体访问要遵守额外的规则:

1. __attribute__((bitband)) 只能作用于位结构体,不能作用于联合体。
2. bitband必须通过匿名方式实现作用于整体,否则不是真正的bitband操作。
3. 要设置#pragma anon_unions,才能实现匿名。
4. 要对变量指定具体地址,如*(volatile mytype*)xxxx。

前三点容易接受,但是第四点则让人觉得很勉强。说明bitband不像xdata,idara或bdata一样好用,编译器不能为你分配bitband地址。就算你只想定义一组标志,不想关心具体地址,也不行。让人担心的两点:“可否整体访问”和“可否自动分配地址”,解决了一点,仍有一点遗憾。还请高手接着出招。

出0入93汤圆

发表于 2012-12-26 07:19:00 | 显示全部楼层
xizi 发表于 2012-12-26 04:43
我手头的计算机没有MDK,我只能到单位去试验。但是看了你对我的代码逐行点评,我知道了我错在哪里。我诚 ...

第四点是比较讨厌,编译器不会自动分配,没有指定范围时他总是默认使用整个RAM区。
而且,即使使用了__attribute__((at()))限定,也只在链接期起作用,不在编译期起作用。
总之,使用起来确实很麻烦,还不如自己写宏手动对应到bitband区,但是又没有那么直观了。

位带实际上类似于51的bdata,而不是xdata。51中位访问也要手动分配到bdata(通过bti、sbit指定),MDK中我暂时还没有找到这样的方法,只好宏定义强制指向bitband区,还要再仔细找找。

出0入93汤圆

发表于 2012-12-26 07:20:58 | 显示全部楼层
Lavion 发表于 2012-12-25 23:05
仅仅适用与M3, M4。除了特殊应用,很少用到。
变量定义的时候,要注意对齐和边界
不过从技术角度讲,顶 ...

不是吧,仅仅用于M3、M4是硬件决定的,其他的不支持是因为硬件不支持。
变量定义时不需要对齐的,因为位访问不可能越过边界的,编译器总是能够正确的寻址。

出0入0汤圆

发表于 2012-12-26 09:24:17 | 显示全部楼层
貌似很强大,收了学习学习

出0入0汤圆

发表于 2012-12-28 03:26:18 | 显示全部楼层
本帖最后由 xizi 于 2012-12-28 04:01 编辑
takashiki 发表于 2012-12-26 07:19
第四点是比较讨厌,编译器不会自动分配,没有指定范围时他总是默认使用整个RAM区。
而且,即使使用了__at ...


51的位操作要简单得多。关于bit和sbit,你描述得不够准确。bit是不需要手动分配地址的,只有sbit需要手动分配地址,但也可以通过bdata绕过手动分配地址:

bit mybit1;//编译器自动给mybit1分配bdata空间的一位

bdata uint8_t byte1;//编译器自动给byte1分配bdata空间的一字节
  sbit mybit2 = byte1^0;//指向自动分配地址的byte1的0位
  sbit mybit3 = byte1^1;//指向自动分配地址的byte1的1位
  sbit mybit4 = byte1^2;//指向自动分配地址的byte1的2位
  sbit mybit5 = byte1^3;//指向自动分配地址的byte1的3位
  sbit mybit6 = byte1^4;//指向自动分配地址的byte1的4位
  sbit mybit7 = byte1^5;//指向自动分配地址的byte1的5位
  sbit mybit8 = byte1^6;//指向自动分配地址的byte1的6位
  sbit mybit9 = byte1^7;//指向自动分配地址的byte1的7位

mybit1到mybit9均可单独访问,而通过byte1就可以实现对bit2到bit9的整体访问。

出0入0汤圆

发表于 2013-1-19 16:11:28 | 显示全部楼层
mark,mdk下的位于操作

出0入0汤圆

发表于 2013-5-15 21:35:41 | 显示全部楼层
BitBand位带,最早知道这个位带的概念是在阅读正点原子大哥的STM32开发板手册里,然后顺着指引查看了CM3权威指南的相关部分,对位带有了大体的认识,LPC1768也是CM3内核的,和STM32有相同之处。这个位带其实在51单片机里面已经用了,操作起来还是很方便的,以前我们对一个寄存器的单个位操作是,通常的作法是先把这个寄存器读回来,然后再修改相应的位,最后在写回去,有了位带操作后,我们可以直接操作这个位。
    关于位带操作的GPIO的速度:经过三牛的测试,位带的操作速度,比直接操作整个IO口寄存器快45%左右,这个是用示波器直接测出来,可能和理论上有偏差。
    LPC1768的CPU时钟100M,直接操作寄存器IO口输出为6.25Mhz,通过位带操作11.2Mhz,因为用的是C语言,这个速度这数据手册上有很大差别,这里仅仅是说明通过位带操作比直接操作要快。
看看位带操作的一些宏定义,这个是从正点原子那里修改而来的
//位带操作,实现51类似的GPIO控制功能
//具体实现思想,参考<<CM3权威指南>>第五章(87页~92页).
//IO口操作宏定义
#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))

//IO口地址映射
//FIOPIN 地址,这里LPC1768和STM32有点不一样,
//LPC1768的 FIOPIN 直接反映端口的状态,不分输入还是输出
#define     GPIO0_FIOPIN    (LPC_GPIO0_BASE + 0x00014)
#define     GPIO1_FIOPIN    (LPC_GPIO1_BASE + 0x00014)
#define     GPIO2_FIOPIN    (LPC_GPIO2_BASE + 0x00014)
#define     GPIO3_FIOPIN    (LPC_GPIO3_BASE + 0x00014)
#define     GPIO4_FIOPIN    (LPC_GPIO4_BASE + 0x00014)

#define     GPIO0(n)            BIT_ADDR(GPIO0_FIOPIN,n)
#define     GPIO1(n)            BIT_ADDR(GPIO1_FIOPIN,n)
#define    GPIO2(n)            BIT_ADDR(GPIO2_FIOPIN,n)
#define     GPIO3(n)            BIT_ADDR(GPIO3_FIOPIN,n)
#define     GPIO4(n)            BIT_ADDR(GPIO4_FIOPIN,n)  

#define LED0 GPIO3(25)
于是我们控制LED0就可以这样些了
#define LED0_ON()    LED0 = 0;//低电平LED亮
#define LED0_OFF()    LED0 = 1;//高电平LED灭
和51单片机一样操作啦
三牛电子工作室:http://www.sanliu85.com/

出0入0汤圆

发表于 2013-10-25 23:31:37 | 显示全部楼层
wuzhujian 发表于 2010-11-26 06:26
如果采用这种方式操作位区,片内功能块的定义需要全部重新编写(不能使用库了),就形成了新的库,效率比ST ...

这个只能在MDK上用的话,估计只有自己改哈

出0入0汤圆

发表于 2014-3-28 14:10:11 | 显示全部楼层
不太懂,先mark下

出0入0汤圆

发表于 2014-4-12 16:25:45 | 显示全部楼层
mark, yao ding

出110入8汤圆

发表于 2014-4-12 19:22:43 | 显示全部楼层
貌似C对位区狠敏感,这样可能会大大的降低程序的可移植性!

出0入0汤圆

发表于 2014-4-12 19:41:00 | 显示全部楼层
又学了一招

出0入0汤圆

发表于 2014-4-13 16:38:00 | 显示全部楼层
wuzhujian 发表于 2011-2-24 23:21
STM32F10x的寄存器文件(修改过的)。
点击此处下载 ourdev_618284JGMW9Q.rar(文件大小:16K) (原文件名:stm ...

大公无私,佩服之...

出0入0汤圆

发表于 2014-8-11 15:51:29 | 显示全部楼层
还没试,不过这样倒是比较节省代码。赞一个,看来还是要不断深入学习MDK编译器参考手册啊!

出0入0汤圆

发表于 2015-4-17 19:55:44 | 显示全部楼层
bitband 位带操作

出0入0汤圆

发表于 2016-11-12 09:24:34 | 显示全部楼层
吊炸天,还是6年前的帖子

出0入0汤圆

发表于 2016-11-12 09:42:23 | 显示全部楼层
即使是六年前的贴子 现在看来还是给我焕然一新的感觉

还是有很多需要学习的地方啊

出0入0汤圆

发表于 2020-8-7 16:01:55 | 显示全部楼层
takashiki 发表于 2012-12-24 13:41
胡说。

以楼主第一楼的代码为例,改成:


请教一下大侠,上面的联合结构体,如果我要用指针访问位域区应该怎么办,

出0入93汤圆

发表于 2020-8-8 05:55:35 | 显示全部楼层
hefq 发表于 2020-8-7 16:01
请教一下大侠,上面的联合结构体,如果我要用指针访问位域区应该怎么办, ...


很多年不用M3了都忘了,低端的M0根本没有位带,高端的从来不关注它了
按照C规范,位域是无法进行指针访问的。位带这样子写成位域大概、也许也不能指针访问吧,我也懒得试了。这里有个宏实现的,你可以看一下
  1. #define BitBandPtr(Addr, Bit) ((volatile int*)(((int)(Addr) & 0x60000000) + 0x02000000 + (int)(Addr) * 0x20 + (Bit) * 4))
复制代码


出0入0汤圆

发表于 2020-8-8 13:52:49 | 显示全部楼层
takashiki 发表于 2020-8-8 05:55
很多年不用M3了都忘了,低端的M0根本没有位带,高端的从来不关注它了
按照C规范,位域是无法进行指针访问 ...

自己算地址是能用指针访问,就是要重新算一遍地址
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-4-26 19:43

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表