我试图用一个LED矩阵来编程Arduino。我需要在绘制每一行之前准备好数据,但是过程中最内部的循环太慢了。屏幕目前闪烁。循环应该在500us以下完成。 Arduino有一个84MHz的Cortex-M3 ARM处理器。
这是我需要如何重新组合输出位的概念:
5位颜色数据:
R1=12, G1=4, B1=7, R2=0, G2=2, B2=27
下一步是创建一个连续1的32位流。 1的数量由颜色值给出:
r1 = 0b00000000000000000000111111111111
g1 = 0b00000000000000000000000000001111
b1 = 0b00000000000000000000000001111111
r2 = 0b00000000000000000000000000000000
g2 = 0b00000000000000000000000000000011
b2 = 0b00000111111111111111111111111111
最后一步是将每10个像素的第n位(总共30个颜色值)重新组合成32位整数:
pack1 = 0b00 ... 111011
pack2 = 0b00 ... 111011
pack3 = 0b00 ... 111001
pack4 = 0b00 ... 111001
pack5 = 0b00 ... 101001
...
这是代码:
// In my case scanwidth is 64*2 (64 is the width of the LED matrix and two lines are scanned at once)
for ( i=0; i<scanwidth/5; i++) { // each run uses 5 upper and 5 lower pixels
data = *lineptr++; // each int in the line buffer contains 2*15-bit inverted color data (red = 31-red etc.)
p1uR = 0x7FFFFFFF >> (data >> 26); // pixel 1 of upper line red channel
p1uG = 0x7FFFFFFF >> (data >> 21 & 0b11111);
p1uB = 0x7FFFFFFF >> (data >> 16 & 0b11111);
p1lR = 0x7FFFFFFF >> (data >> 10 & 0b11111);
p1lG = 0x7FFFFFFF >> (data >> 5 & 0b11111);
p1lB = 0x7FFFFFFF >> (data & 0b11111);
data = *lineptr++;
p2uR = 0x7FFFFFFF >> (data >> 26);
p2uG = 0x7FFFFFFF >> (data >> 21 & 0b11111);
p2uB = 0x7FFFFFFF >> (data >> 16 & 0b11111);
p2lR = 0x7FFFFFFF >> (data >> 10 & 0b11111);
p2lG = 0x7FFFFFFF >> (data >> 5 & 0b11111);
p2lB = 0x7FFFFFFF >> (data & 0b11111);
data = *lineptr++;
p3uR = 0x7FFFFFFF >> (data >> 26);
p3uG = 0x7FFFFFFF >> (data >> 21 & 0b11111);
p3uB = 0x7FFFFFFF >> (data >> 16 & 0b11111);
p3lR = 0x7FFFFFFF >> (data >> 10 & 0b11111);
p3lG = 0x7FFFFFFF >> (data >> 5 & 0b11111);
p3lB = 0x7FFFFFFF >> (data & 0b11111);
data = *lineptr++;
p4uR = 0x7FFFFFFF >> (data >> 26);
p4uG = 0x7FFFFFFF >> (data >> 21 & 0b11111);
p4uB = 0x7FFFFFFF >> (data >> 16 & 0b11111);
p4lR = 0x7FFFFFFF >> (data >> 10 & 0b11111);
p4lG = 0x7FFFFFFF >> (data >> 5 & 0b11111);
p4lB = 0x7FFFFFFF >> (data & 0b11111);
data = *lineptr++;
p5uR = 0x7FFFFFFF >> (data >> 26);
p5uG = 0x7FFFFFFF >> (data >> 21 & 0b11111);
p5uB = 0x7FFFFFFF >> (data >> 16 & 0b11111);
p5lR = 0x7FFFFFFF >> (data >> 10 & 0b11111);
p5lG = 0x7FFFFFFF >> (data >> 5 & 0b11111);
p5lB = 0x7FFFFFFF >> (data & 0b11111);
index = i;
for (j=0; j<31; j++){ // loop over all 30 bits
index += (scanwidth/5+1);
scanbuff[index] = (p5uR>>j&1)<<29 | (p5uG>>j&1)<<28 | (p5uB>>j&1)<<27 | (p5lR>>j&1)<<26 | (p5lG>>j&1)<<25 | (p5lB>>j&1)<<24
| (p4uR>>j&1)<<23 | (p4uG>>j&1)<<22 | (p4uB>>j&1)<<21 | (p4lR>>j&1)<<20 | (p4lG>>j&1)<<19 | (p4lB>>j&1)<<18
| (p3uR>>j&1)<<17 | (p3uG>>j&1)<<16 | (p3uB>>j&1)<<15 | (p3lR>>j&1)<<14 | (p3lG>>j&1)<<13 | (p3lB>>j&1)<<12
| (p2uR>>j&1)<<11 | (p2uG>>j&1)<<10 | (p2uB>>j&1)<<9 | (p2lR>>j&1)<<8 | (p2lG>>j&1)<<7 | (p2lB>>j&1)<<6
| (p1uR>>j&1)<<5 | (p1uG>>j&1)<<4 | (p1uB>>j&1)<<3 | (p1lR>>j&1)<<2 | (p1lG>>j&1)<<1 | (p1lB>>j&1);
}
}
我认为改善外循环是不必要的。我确实试图展开内循环,但它没有明显改善。
Cortex-M3可以在一个时钟周期内完成移位和逻辑运算。我估计外环和内环需要大约51000个时钟周期(600us)。
我可以用标准C ++代码改进吗?在内联汇编中是否可以进行任何改进?
答案 0 :(得分:6)
一些Cortex-M 3黑魔法的时间。
#include <cstdint>
#include <memory>
#include <cstring>
volatile char *const bitband_packed = (volatile char*)0x20000000;
volatile uint32_t *const bitband_exploded = (volatile uint32_t*)0x22000000;
static inline void transform_32_32(uint32_t buff[32]) {
const std::size_t size = sizeof(buff[0])*32;
volatile char *const tmp = bitband_packed;
std::memcpy(const_cast<char*>(tmp), buff, size);
for(std::size_t i = 0; i < 32; i++) {
for(std::size_t j = i + 1; j < 32; j++) {
std::swap(bitband_exploded[(32 * i + j)], bitband_exploded[(32 * j + i)]);
}
}
std::memcpy(buff, const_cast<char*>(tmp), size);
}
void transform_pwm_32channel_5bit(const uint8_t input[32], uint32_t output[32]) {
for(std::size_t i = 0; i < 32; i++) {
output[i] = 0xffffffff >> input[i];
}
transform_32_32(output);
}
Cortex-M系列有一个很好的功能叫Bit-Banding。这样就可以实现非常有效的按位矩阵变换,这恰好就是你有效地进行bitbang所需要的。
变换应该每位约3个周期(compiled on GCC 6.3 with -funroll-loops)执行,因此总共应该只有大约12k个周期,或大约150us。
唯一的收获?这假设您的特定Cortex-M 3实际上支持位带功能。我没有机会在Arduino上测试这个。