关键代码如下
通过串口驱动接口,注册串口接收回调函数,uart_rx_callback
该回调函数中如果收到串口数据,长度非0,则更新全局变量uart_rx_len
主循环中再检查全局变量uart_rx_len
,如果大于0说明收到了串口数据,将收到的数据再发送出去,实现简单的串口回环测试。
static int uart_rx_len = 0;
void uart_rx_callback(const void *buffer, uint32_t length)
{
if(length >0)
{
uart_rx_len = length;
}
}
int main(void)
{
......
debug_uart_init(IOT_UART_PORT_1);
uint8_t buffer[64];
iot_uart_register_rx_callback(IOT_UART_PORT_1,buffer,sizeof(buffer),uart_rx_callback);
while(1)
{
if(uart_rx_len > 0)
{
iot_uart_write_buffer(IOT_UART_PORT_1,buffer,uart_rx_len);
uart_rx_len=0;
}
}
}
现象是并没有实现上述回环测试的功能。
于是进行调试,先确认是否进入了接收处理,
b uart_rx_callback
发现可以进入该回调函数,说明收到了数据。
step
单步运行到执行完uart_rx_len = length;
,再查看该变量的值
(gdb) p uart_rx_len
$2 = 1
也确实收到了一个字节。
然后继续往下看,看如下条件是否进入
if(uart_rx_len > 0)
{
iot_uart_write_buffer(IOT_UART_PORT_1,buffer,uart_rx_len);
uart_rx_len=0;
}
b main.c:146
在iot_uart_write_buffer(IOT_UART_PORT_1,buffer,uart_rx_len
所在行146行,打断点。
发现进不了该断点。
这里就比较奇怪了,前面uart_rx_len
确实是1,了但是这里条件却进不去,其他地方也没有写uart_rx_len
的地方。
那么只有继续看该处代码对应的汇编代码
先在uart_rx_callback
前打断点,串口接收一个字节触发该回调执行。再在 if(uart_rx_len > 0)
所在的行144行前打断点,b main.c:144
,c
运行到该处。
此时看到uart_ex_len
的值是1正确的。
(gdb) p uart_rx_len
$2 = 1
layout split
打开汇编和C对照窗口。
查看变量uart_rx_len
的地址,为0x20300c8
(gdb) p &uart_rx_len
$4 = (int *) 0x20300c8 < uart_rx_len >
(gdb) info reg s1
s1 0x2030000 33751040
S1
寄存器的值设置为0x2030000, lui s1,0x2030
lui的u表示up(高20位),加载0x2030到S1的高20位。
lw a2,200(S1)
即将S1对应的地址偏移200(0xC8)地址处的值加载到a2寄存器。
正好是获取0x2030C8(uart_rx_len)的值到A2,
然后再执行
blez a2,0x20001ba进行判断uart_rx_len和0的值比较,判断是否往下执行还是在此死循环。
初看没问题,一细看就有端倪了。
假设一开始uart_rx_len=0,
那么后面始终执行的是一条语句,blez a2,0x20001ba,a2寄存器的值不再更新了,这就有问题了,内存中0x2030C8(uart_rx_len)的值变了,但是寄存器a2的值不再变化。
这就是编译器自作主张优化,生成的代码没有继续去从内存0x2030C8(uart_rx_len)处更新值到a2寄存器了。理论上是需要再次执行上述lw a2,200(S1)
指令的。这就编译器优化导致的问题。
我们修改
static int uart_rx_len = 0;
改为
`static volatile int uart_rx_len = 0;``
再来看汇编代码。
可以看到如果a5小于0,会跳转到addi s1,s2,200处执行再继续lw a5,0(S1)处加载uart_rx_len的值到a5,会不断从内存处更新值到寄存器。
这就是volatile的作用,加了volatile后编译器始终,会从内存中更新值到寄存器,而不会自作主张使用寄存器中缓存的值。
默认SCons/riscv_tools.py中是Os优化,
CCFLAGS = common_flags + [
"-Os",
]
不加volatile且优化改为
-O3,-O2,-O1,-O0分别看一下。
可以看到-O1优化编译就进行了优化,后面-O2,-O3就不用看了。
-O0
-O1
这里函数uart_rx_callback写了变量uart_rx_len
void uart_rx_callback(const void *buffer, uint32_t length)
{
if(length >0)
{
uart_rx_len = length;
}
}
且uart_rx_callback函数也作为回调函数使用了,理论上编译器应该指导uart_rx_len会被改写,不应该作此优化。
手动调用以下uart_rx_callback
uart_rx_callback(0, 0);
debug_uart_init(IOT_UART_PORT_1);
还是一样的优化了
看来编译器还是聪明过头了。
这里主要是
while(1)后面第一条语句就是判断uart_rx_len,如果之前还有其他语句,则编译器可能不会优化了。
while(1)
{
if(uart_rx_len > 0)
{
iot_uart_write_buffer(IOT_UART_PORT_1,buffer,uart_rx_len);
uart_rx_len=0;
}
}
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !