项目场景:
一个具有使用本地数据保存的产品,在测试过程中发现数据超过65535字节时,读取数据记录的数据字节数不对,总是丢失高16位的记录数.
问题描述:
问题代码如下:
uint32_t EEread_DataAdd(void)
{
uint8_t temp[4];
uint16_t high,low;
uint32_t ndata;
temp[0] = FLASH_0_read_eeprom_byte((uint16_t)DataAdd_EEaddr);
temp[1] = FLASH_0_read_eeprom_byte((uint16_t)DataAdd_EEaddr + 1);
temp[2] = FLASH_0_read_eeprom_byte((uint16_t)DataAdd_EEaddr + 2);
temp[3] = FLASH_0_read_eeprom_byte((uint16_t)DataAdd_EEaddr + 3);
high = (uint16_t)((uint16_t)(temp[0] << 8) | temp[1]);
low = (uint16_t)((uint16_t)(temp[2] << 8) | temp[3]);
ndata = (uint32_t)((uint32-t)(high << 16)|low);
return (ndata);
}
以上代码在编绎时会提示移位超出范围的警告:
Warning left shift count >= width of type [-Wshift-count-overflow]
当时也没有注意,因为代码里很多从串口接收数据回填的函数都没有出错。直到测试数据大于65535后才发现这个位置的数据返回错误,高16位一直返回的是0;
原因分析:
为了找出问题点,跟踪调试从EEPROM里读取记录条数回填函数,并用串口打印出来发现,数据出错的地方,就是上面左移16位的位置。
测试代码如下:
uint32_t EEread_DataAdd(void)
{
uint8_t temp[4];
uint16_t high,low;
uint32_t ndata;
temp[0] = FLASH_0_read_eeprom_byte((uint16_t)DataAdd_EEaddr);
temp[1] = FLASH_0_read_eeprom_byte((uint16_t)DataAdd_EEaddr + 1);
temp[2] = FLASH_0_read_eeprom_byte((uint16_t)DataAdd_EEaddr + 2);
temp[3] = FLASH_0_read_eeprom_byte((uint16_t)DataAdd_EEaddr + 3);
high = (uint16_t)((uint16_t)(temp[0] << 8) | temp[1]);
low = (uint16_t)((uint16_t)(temp[2] << 8) | temp[3]);
UART_Send_byte((uint8_t)(high>>8));
UART_Send_byte((uint8_t)(high&0xff));
UART_Send_byte((uint8_t)(low>>8));
UART_Send_byte((uint8_t)(low&0xff));
ndata = (uint32_t)(high << 16)|low;
UART_Send_byte((uint8_t)(ndata>>24));
UART_Send_byte((uint8_t)(ndata>>16));
UART_Send_byte((uint8_t)(ndata>>8));
UART_Send_byte((uint8_t)(ndata&0xff));
return (ndata);
}
通过串口打印信息发现,在移位操作前HIGH和LOW的数据是正常的,而在执行完
ndata = (uint32_t)(high << 16)|low;命令后,打印出的数据高16位一直为0.
解决方案:
因为是移位操作在编绎时出现了警告,所以分析是数据移位导致的问题,所以试着改成如下代码后测试问题解决:
ndata = (uint32_t)(high *65535)+low;
ndata = ndata +1;//这里要注意,因为是16位数乘,所以最终结果一定要加1,以满足65536的要求。
个人考虑系统是AVR的8位单片机,乘除运算比较耗资源,所以我最终经过测试后选用的是左移8位两次的方法。
uint32_t EEread_DataAdd(void)
{
uint8_t temp[4];
uint16_t high,low;
uint32_t ndata;
temp[0] = FLASH_0_read_eeprom_byte((uint16_t)DataAdd_EEaddr);
temp[1] = FLASH_0_read_eeprom_byte((uint16_t)DataAdd_EEaddr + 1);
temp[2] = FLASH_0_read_eeprom_byte((uint16_t)DataAdd_EEaddr + 2);
temp[3] = FLASH_0_read_eeprom_byte((uint16_t)DataAdd_EEaddr + 3);
high = (uint16_t)((uint16_t)(temp[0] << 8) | temp[1]);
low = (uint16_t)((uint16_t)(temp[2] << 8) | temp[3]);
ndata = (uint32_t)(high << 8); //左移8位
ndata = (ndata<< 8)|low; //再次左移8位,相当于左移16位,避开直接左移16位出现结果错误的问题
return (ndata);
}