这是用于AVR(Arduino硬件)的WS2812B RGB LED条带驱动代码。我在编译器优化部分代码时遇到了一些问题。我尝试了-O1, -O2, -Os
,结果相同。我无法使用-O0
,因为延迟不起作用。
这是我的ws_driverm.h
文件:
#pragma once
#include <util/delay_basic.h>
#include <avr/io.h>
#include "lib/pins.h"
#define WS_T_1H 800
#define WS_T_1L 450
#define WS_T_0H 200
#define WS_T_0L 850
#define WS_T_LATCH 7000
/** Latch and display the RGB values */
#define ws_show(io) do { pin_low(io_pack(io)); delay_ns_c(WS_T_LATCH, 0); } while(0)
/** Send one byte to the RGB strip */
#define ws_send_byte(io, bb) do { \
for (int8_t __wsba_i = 7; __wsba_i >= 0; --__wsba_i) { \
if (bb & (1 << __wsba_i)) { \
pin_high(io_pack(io)); delay_ns_c(WS_T_1H, -2); \
pin_low(io_pack(io)); delay_ns_c(WS_T_1L, -10); \
} else { \
pin_high(io_pack(io)); delay_ns_c(WS_T_0H, -2); \
pin_low(io_pack(io)); delay_ns_c(WS_T_0L, -10); \
} \
} \
} while(0)
/** Send RGB color to the strip */
#define ws_send_rgb(io, r, g, b) do { \
ws_send_byte(io_pack(io), g); \
ws_send_byte(io_pack(io), r); \
ws_send_byte(io_pack(io), b); \
} while(0)
我对它是宏不满意,但我尝试使用指向IO端口,端口地址等的指针都失败了。
io
是我的针脚处理系统的一部分,只是忽略它,它与问题无关。
这里是问题(在主要方面)
当我将其用于保证更改的变量或将其标记为volatile时,它会按预期工作:
volatile uint8_t r = 0;
volatile uint8_t g = 255;
volatile uint8_t b = 0;
ws_send_rgb(PIN_WS, r, g, b);
ws_show(PIN_WS);
// results in GREEN color
当变量不是volatile时,或者我只是在宏调用中放入数字时,它不起作用:
uint8_t r = 0;
uint8_t g = 255;
uint8_t b = 0;
ws_send_rgb(PIN_WS, r, g, b);
ws_show(PIN_WS);
// results in YELLOW color
结果相同:
ws_send_rgb(PIN_WS, 0, 255, 0);
ws_show(PIN_WS);
// results in YELLOW color
很明显,由于某种原因优化了宏所产生的代码,如果它的常数和零(至少它看起来那样 - 我不确定为什么LED显示黄色。)
有趣: - &gt;如果我使用1
代替0
作为颜色,它会按预期工作。
我的宏有什么问题,我该如何解决? 此时肮脏的解决方法很好。
答案 0 :(得分:1)
我现在用这种方式解决了 - 效果还不错:
我在循环变量中添加了volatile
关键字。
不知道为什么会有所帮助,但确实如此。
/** Send one byte to the RGB strip */
#define ws_send_byte(io, bb) do { \
for (volatile int8_t __wsba_i = 7; __wsba_i >= 0; --__wsba_i) { \
if (bb & (1 << __wsba_i)) { \
pin_high(io_pack(io)); delay_ns_c(WS_T_1H, -2); \
pin_low(io_pack(io)); delay_ns_c(WS_T_1L, -10); \
} else { \
pin_high(io_pack(io)); delay_ns_c(WS_T_0H, -2); \
pin_low(io_pack(io)); delay_ns_c(WS_T_0L, -10); \
} \
} \
} while(0)
答案 1 :(得分:0)
我猜pin_high
和pin_low
写入内存地址。
正在发生的事情是(实际上)编译器被赋予这样的代码:
char *address = ...;
*address = something;
*address = somethingelse;
它正在丢弃第二行,因为如果address
指向正常内存,那么这什么都不做。但事实并非如此。你对记忆的写作有副作用,这很重要。
为了避免这种情况,您应该真正修复pin_high
和pin_low
以包含memory barrier或类似内容。但是,您的volatile
关键字会对其进行修复。我怀疑它可能会意外地修复它,因为它实际上是内存地址(或者说它的内容)应该是volatile
,而不是写入它的内容。
这里可能的路由是引入一个C函数,该函数什么也不做,希望它充当内存屏障(即编译器可能使用写入值的东西)。你需要这个不被优化。例如:
static void donothingroutine() {}
volatile void (*donothing)() = &donothingroutine;
然后在宏中添加:
donothing();
当指向例程的指针被声明为volatile
时,从编译器的角度来看donothing()
它可能是一个访问写入的'memory'的例程,所以它无法优化写入。所以,在你的宏观中洒下它。