使用常量值时,部分宏被优化掉了

时间:2014-12-22 12:53:21

标签: c macros avr avr-gcc

这是用于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作为颜色,它会按预期工作。

我的宏有什么问题,我该如何解决? 此时肮脏的解决方法很好。

2 个答案:

答案 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_highpin_low写入内存地址。

正在发生的事情是(实际上)编译器被赋予这样的代码:

char *address = ...;
*address = something;
*address = somethingelse;

它正在丢弃第二行,因为如果address指向正常内存,那么这什么都不做。但事实并非如此。你对记忆的写作有副作用,这很重要。

为了避免这种情况,您应该真正修复pin_highpin_low以包含memory barrier或类似内容。但是,您的volatile关键字会对其进行修复。我怀疑它可能会意外地修复它,因为它实际上是内存地址(或者说它的内容)应该是volatile,而不是写入它的内容。

这里可能的路由是引入一个C函数,该函数什么也不做,希望它充当内存屏障(即编译器可能使用写入值的东西)。你需要这个不被优化。例如:

static void donothingroutine() {}
volatile void (*donothing)() = &donothingroutine;

然后在宏中添加:

donothing();

当指向例程的指针被声明为volatile时,从编译器的角度来看donothing()可能是一个访问写入的'memory'的例程,所以它无法优化写入。所以,在你的宏观中洒下它。