求助:STM32位段高效率的操作

auto_dut   2010-8-16 08:15 楼主


11111111
11111111
11111111
11111111
上面是四个字节的源数据,需要转换成下面的8个字节的数据输出

Tempdata1:10101010(源数据的Bit7组成
Tempdata2:10101010  (源数据的Bit6组成)
Tempdata3:10101010  (源数据的Bit5组成)
Tempdata4:10101010  (源数据的Bit4组成)
Tempdata5:10101010  (源数据的Bit3组成)
Tempdata6:10101010  (源数据的Bit2组成)
Tempdata7:10101010  (源数据的Bit1组成)
Tempdata8:10101010  (源数据的Bit0组成)


tempdata1-8只是为了表达功能示意,使用的变量。
实际上只使用两个变量暂存输出就可以了。函数如下

for (RowCount=0;RowCount<4096;RowCount++)
{
    tempdata1=((*psrc)&0x80)|(((*psrc1)&0x80)>>2);
    tempdata2=(((*psrc2)&0x80)>>4)|(((*psrc3)&0x80)>>6);
    GPIOA->ODR =tempdata1|tempdata2;
    CS_H;
    CS_L;
   
    tempdata1=(((*psrc)&0x40)<<1)|(((*psrc1)&0x40)>>1);
    tempdata2=(((*psrc2)&0x40)>>3)|(((*psrc3)&0x40)>>5);
    GPIOA->ODR =tempdata1|tempdata2;
    CS_H;
    CS_L;
   
    tempdata1=(((*psrc)&0x20)<<2)|(((*psrc1)&0x20));
    tempdata2=(((*psrc2)&0x20)>>2)|(((*psrc3)&0x20)>>4);
    GPIOA->ODR =tempdata1|tempdata2;
    CS_H;
    CS_L;
   
    tempdata1=(((*psrc)&0x10)<<3)|(((*psrc1)&0x10)<<1);
    tempdata2=(((*psrc2)&0x10)>>1)|(((*psrc3)&0x10)>>3);
    GPIOA->ODR =tempdata1|tempdata2;
    CS_H;
    CS_L;
   
    tempdata1=(((*psrc)&0x08)<<4)|(((*psrc1)&0x08)<<2);
    tempdata2=(((*psrc2)&0x08))|(((*psrc3)&0x08)>>2);
    GPIOA->ODR =tempdata1|tempdata2;
    CS_H;
    CS_L;
   
    tempdata1=(((*psrc)&0x04)<<5)|(((*psrc1)&0x04)<<3);
    tempdata2=(((*psrc2)&0x04)<<1)|(((*psrc3)&0x04)>>1);
    GPIOA->ODR =tempdata1|tempdata2;
    CS_H;
    CS_L;
   
    tempdata1=(((*psrc)&0x02)<<6)|(((*psrc1)&0x02)<<4);
    tempdata2=(((*psrc2)&0x02)<<2)|(((*psrc3)&0x02));
    GPIOA->ODR =tempdata1|tempdata2;
    CS_H;
    CS_L;
   
    tempdata1=(((*psrc)&0x01)<<7)|(((*psrc1)&0x01)<<5);
    tempdata2=(((*psrc2)&0x01)<<3)|(((*psrc3)&0x01)<<1);
    GPIOA->ODR =tempdata1|tempdata2;
    CS_H;
    CS_L;
    psrc++;
    psrc1++;
    psrc2++;
    psrc3++;
}

这种方法,1秒钟,大概可以执行160次,距离目标差很多。
利用MDK看了下汇编,M3核心有UBFX,BFI,BFC这几个位段
操作指令,汇编里面并没有使用。所以在这里请教下各位大侠有办法能
提高执行速度吗?

回复评论 (36)

                                 focus on
点赞  2010-8-16 09:47
                                 这个应用应该用bitbanding功能,FOR的循环都可以少很多。
点赞  2010-8-16 10:27
STM32的位段操作很适合高效地处理这种情况。试试下面的算法,我没有测试过,不保证正确,但思路是正确的。

uint8_t src[4], result[8];
uint32_t temp, *psrc, *presult;

temp = (uint32_t)(&src - 0x22000000);
psrc = (uint32_t)(0x22000000 + (temp * 32));  // 得到src[0]的位段地址
temp = (uint32_t)(&result - 0x22000000);
presult = (uint32_t)(0x22000000 + (temp  * 32));  // 得到result[0]的位段地址

for (temp = 0; temp < 8; temp++) {
  result[temp] = 0; // 目标字节清0
  presult[7] = psrc[0];  // 设置目标字节最高位 = 最低原数据字节位
  presult[5] = psrc[8];  // 设置目标字节第5位 = 第2原数据字节位
  presult[3] = psrc[16];// 设置目标字节第3位 = 第3原数据字节位
  presult[1] = psrc[24];// 设置目标字节第1位 = 第4原数据字节位
  presult += 8;  // 指向下一个目标字节
  psrc++; // 指向下一个原数据位
}

STM32_Bitband_Usage.GIF (23.24 KB)

点赞  2010-8-16 10:36
给你看看ST某年的一个培训材料吧,由易入深


点赞  2010-8-16 10:49
极具参考价值,腾出时间来我也研究下,我要解决的问题是把我下面的函数运行效率提高

//将矩形显存从源地址拷贝到目标地址,规定了起始地址、目标地址和矩形的面积:
//pFrom      - 源显存首地址,即 (xFrom, yFrom) 为 (0, 0) 的位置
//xFrom      - 源矩形左下角 X 轴坐标(基于像素)
//yFrom      - 源矩形左下角 Y 轴坐标(基于像素)
//HeightFrom - 源显存行高度(该高度跟定位同行相邻字节有关)
//pTo        - 目标显存首地址,即 (xTo, yTo) 为 (0, 0) 的位置
//xTo        - 目标矩形左下角 X 轴坐标(基于像素)
//yTo        - 目标矩形左下角 Y 轴坐标(基于像素)
//HeightTo   - 目标显存行高度(该高度跟定位同行相邻字节有关)
//width      - 矩形像素宽度
//height     - 矩形像素高度
//
//目标:     Y
//           ...................................................................
//           ...................................................................
//           ...................................................................
//           ...................................**********************..........
//           ...................................**********************..........
//           ...................................**********************..........
//           ...................................**********************..........
//         0 ................................................................... X
//           0
//
//源:       Y
//           ...................................................................
//           ...................................................................
//           ...................................................................
//           ...................................................................
//           ...................................................................
//           ...................................................................
//           ...................................................................
//           ...................................................................
//           ...................................................................
//           ..................**********************...........................
//           ..................**********************...........................
//           ..................**********************...........................
//           ..................**********************...........................
//         0 ................................................................... X
//           0
//上面所示(单位:像素),将标有'*'的源矩形显存拷贝到目标矩形显存,则:
//xFrom      - 18
//yFrom      - 1
//HeightFrom - 14
//xTo        - 35
//yTo        - 1
//HeightTo   - 8
//width      - 22
//height     - 4
////////////////////////////////////////////////////////////////////////////////
void CopyRectDisplayMemory(u8 *pFrom, u16 xFrom, u16 yFrom, u16 HeightFrom, u8* pTo, u16 xTo, u16 yTo, u16 HeightTo, u16 width, u16 height)
{
    u8  *pRowHeadFrom, *pRowHeadTo;                      //指向源、目标矩形每一行点阵第1个字节的指针
    u8  bitsLeftFrom, bitsRightFrom;                     //源矩形显存每个字节由左右两部分组成
    u8  bitsLeftTo, bitsRightTo;                         //目标矩形显存每个字节由左右两部分组成
    u8  byteFrom, byteBack;                              //源字节显存和目标字节备份显存
    u16 i, j;
   
    bitsLeftFrom  = 8 - xFrom%8;                         //6,源矩形显存每个字节由左(侧字节低)6位和右(侧字节高)2位组成
    bitsRightFrom = xFrom%8;                             //2
   
    bitsLeftTo    = 8 - xTo%8;                           //5,目标矩形显存每个字节由左(侧字节低)5位和右(侧字节高)3位组成
    bitsRightTo   = xTo%8;                               //3
   
    for(i=0; i<height; i++)                              //height行
    {
        StaticScan();
      
        pRowHeadFrom  = pFrom;
        pRowHeadFrom += (xFrom/8)*HeightFrom;
        pRowHeadFrom += yFrom;
        pRowHeadFrom += i;                               //每行源显存第一个(左6位所在)字节位置
        
        pRowHeadTo    = pTo;
        pRowHeadTo   += (xTo/8)*HeightTo;
        pRowHeadTo   += yTo;
        pRowHeadTo   += i;                               //每行目标显存第一个(左5位所在)字节位置
        
        for(j=0; j<(width/8); j++)                       //先复制整8位列
        {                                                //提取源字节数据(左6位+右2位 组成新的字节保存到 byteFrom)
            byteFrom = (*pRowHeadFrom) << bitsRightFrom; //取左边字节的低6位
            pRowHeadFrom += HeightFrom;                  //偏移到右边字节
            byteFrom += (*pRowHeadFrom) >> bitsLeftFrom; //取右边字节的高2位
            
            (*pRowHeadTo) &= (0xff << bitsLeftTo);       //清掉目标字节低5位
            (*pRowHeadTo) += (byteFrom >> bitsRightTo);  //加上源字节高5位
            
            pRowHeadTo += HeightTo;                      //偏移到右边字节
            (*pRowHeadTo) &= (0xff >> bitsRightTo);      //清掉目标字节高3位
            (*pRowHeadTo) += (byteFrom << bitsLeftTo);   //加上源字节低3位
        }
        
        if(width%8)                                      //剩余不足8位需要复制
        {                                                
            byteBack  = (*pRowHeadTo) << bitsRightTo;    //下面备份目标字节
            byteBack += (*(pRowHeadTo + HeightTo)) >> bitsLeftTo;
            byteBack &= (0xff >> (width%8));             //把要填充的目标位清掉
            
            byteFrom = (*pRowHeadFrom) << bitsRightFrom; //取左边字节的低6位
            pRowHeadFrom += HeightFrom;                  //偏移到右边字节
            byteFrom += (*pRowHeadFrom) >> bitsLeftFrom; //取右边字节的高2位
            byteFrom &= (0xff << (8 - width%8));         //只保留要复制的目标位
            
            byteFrom += byteBack;                        //要复制的目标位+备份目标位 组成新字节,复制到目标字节
            
            (*pRowHeadTo) &= (0xff << bitsLeftTo);       //清掉目标字节低5位
            (*pRowHeadTo) += (byteFrom >> bitsRightTo);  //加上源字节高5位
            
            pRowHeadTo += HeightTo;                      //偏移到右边字节
            (*pRowHeadTo) &= (0xff >> bitsRightTo);      //清掉目标字节高3位
            (*pRowHeadTo) += (byteFrom << bitsLeftTo);   //加上源字节低3位
        }
    }
}
点赞  2010-8-16 11:09
所有需要做移位,取BIT位填充字节的应用都适合用BIT BAND。

虽然BIT BAND仍是个读,改,写回的过程,但是许多移位,自加,判断的语句都可以大量减少。
点赞  2010-8-16 11:13
                                 还得研究下细节,STM32的BIT BAND操作效率究竟高在哪些细节,这样心理才有底
点赞  2010-8-16 11:41
还得研究下细节,STM32的BIT BAND操作效率究竟高在哪些细节,这样心理才有底
你可以对照楼主的程序和我写的程序,主要是省却了很多“与”“或”和“移位”运算。
点赞  2010-8-16 11:58


O了,刚简单看了几眼BITBAND的意思,是以32位为最小分段的,那么我想把8位字节A的低5位和8位字节B的高3位组成一个新的字节,跟BITBAND的要求有什么区别?
点赞  2010-8-16 14:53
简单地说,BITBAND就是以“字”的方式访问“位”。

要想把8位字节A的低5位和8位字节B的高3位组成一个新的字节,可能不用BITBAND更快些,否则要做8次数据的读和8次数据的写。
点赞  2010-8-16 15:24
                                 那可能也比我现在的方式快,另外我可以改下我的的数学模型的,就是数据的排列方式,只要BITBAND能提高矩形位域的数据复制效率,现在的效率当屏大了之后不能忍受
点赞  2010-8-16 15:49
那可能也比我现在的方式快,另外我可以改下我的的数学模型的,就是数据的排列方式,只要BITBAND能提高矩形位域的数据复制效率,现在的效率当屏大了之后不能忍受 ...
winloop 发表于 2010-8-16 15:49
点赞  2010-8-16 15:53
                                 
点赞  2010-8-16 16:25
                                 头晕了
点赞  2010-8-16 17:40
根据香水斑竹的思路修改了部分,如下:

uint32 tempcount;
uint32 *target1;

tempcount=(uint32)((&tempdata1)-0x20000000);

target1=(uint32*)(0x22000000+tempcount*32);
//tempdata1别名区的地址,bit0

for (RowCount=0;RowCount<4096;RowCount++)
{
    target1[7]=*psrc>>7;
    target1[5]=*psrc1>>7;
    target1[3]=*psrc2>>7;
    target1[1]=*psrc3>>7;
    GPIOA->ODR =tempdata1;
    CS_H;
    CS_L;
   
    target1[7]=*psrc>>6;
    target1[5]=*psrc1>>6;
    target1[3]=*psrc2>>6;
    target1[1]=*psrc3>>6;
    GPIOA->ODR =tempdata1;
    CS_H;
    CS_L;

    target1[7]=*psrc>>5;
    target1[5]=*psrc1>>5;
    target1[3]=*psrc2>>5;
    target1[1]=*psrc3>>5;
    GPIOA->ODR =tempdata1;
    CS_H;
    CS_L;

    target1[7]=*psrc>>4;
    target1[5]=*psrc1>>4;
    target1[3]=*psrc2>>4;
    target1[1]=*psrc3>>4;
    GPIOA->ODR =tempdata1;
    CS_H;
    CS_L;

    target1[7]=*psrc>>3;
    target1[5]=*psrc1>>3;
    target1[3]=*psrc2>>3;
    target1[1]=*psrc3>>3;
    GPIOA->ODR =tempdata1;
    CS_H;
    CS_L;

    target1[7]=*psrc>>2;
    target1[5]=*psrc1>>2;
    target1[3]=*psrc2>>2;
    target1[1]=*psrc3>>2;
    GPIOA->ODR =tempdata1;
    CS_H;
    CS_L;

    target1[7]=*psrc>>1;
    target1[5]=*psrc1>>1;
    target1[3]=*psrc2>>1;
    target1[1]=*psrc3>>1;
    GPIOA->ODR =tempdata1;
    CS_H;
    CS_L;

    target1[7]=*psrc;
    target1[5]=*psrc1;
    target1[3]=*psrc2;
    target1[1]=*psrc3;
    GPIOA->ODR =tempdata1;
    CS_H;
    CS_L;

    psrc++;
    psrc1++;
    psrc2++;
    psrc3++;
}

后面的源数据我还是暂时通过移位来实现,和之前比较,简捷了很多。
编译OK ,运行,先不管数据组织的对不对!
速度没有提高,每秒的运行次数反而下降了30次左右........
点赞  2010-8-17 15:12
感觉如果把后面的源数据也用BitBand区域形式来访问,速度估计快不到哪里去,猜测!
是我哪里出了问题还是怎么了,香水斑竹还有各位大侠们有空进来帮偶瞅瞅啊......
看看还有什么好办法,谢谢啦!
点赞  2010-8-17 15:17
                                 CS_H和CS_L是什么? 什么信号?
点赞  2010-8-17 15:20
细看了下5楼朋友的 回复.......心凉了..........
BitBand不能减少操作时间

仅仅是简化操作减小代码量......
点赞  2010-8-17 15:22


CS_H,CS_L是一个IO口的操作,CLOCK类的,一高一低
点赞  2010-8-17 15:23
12下一页
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复