codeblocks测试工程下载:https://wwa.lanzoui.com/i2u7tjc
一般情况下,ADC与单片机的通讯方式都是串行的,可能是IIC、SPI等。假设现在有一个24位的ADC,那么在正常工作时,单片机将依次接收到24bit的数据。ADC的数据是24位的,但是没有24位的数据类型。在单片机中,int类型的数据一般是32位的。所以需要一个函数来完成两者的转换。
首先我们要搞清楚计算机中数字的二进制存储形式。对于一个24位数,其计算关系可用下面的公式表达:
对于一个32位数,其计算关系可用下面的公式表达:
显而易见,对于一个正数来说,要拓宽它的位数,只需要在其多出来的位上补0即可。因为只是多项式中多加了几个0而已,并不会改变数据的大小。
而如果我们转换的数据是一个负数,问题就不那么简单了。
首先我们要搞清楚负数是怎么存储的。计算机中的负数是以补码形式存储的,即补码=原码取反+1。就是对正数先取反码,然后再加1。前面我们已经搞清楚了对一个正数该怎么拓宽,即补0。如果对拓宽后的这个正数再进行取反然后加1,那么我们是不是得到了一个位数改变的负数呢?答案肯定是对的。
假设我们现在有一个负数,我们可以先倒推回去,将其变成正数,然后用我们确定正确的方式对这个正数进行转换,然后把正数再变成负数。此时得到的负数位数已经是改变了的。可能有点绕,但确实是可行的。我们会发现,其实最终的效果是在多出来的位上补了1。这是因为我们对正数补0之后取了反,那就相当于直接补1了。
下面开始编写我们的代码。这里定义一个数组adc[3],用来存储ADC的数据。将接收到的三字节数据由低到高依次放置到数组中。即adc[0]放置低字节,adc[1]放置中间的字节,adc[2]放置高字节。
int adc24to32(unsigned char *padc);
此函数的输入参数为数组的地址。函数内部完成数据的转换,返回值为转换之后的32位数据。
在函数内定义一个int型的数据value,然后再定义一个指向value的指针。然后将数组中的数据搬到value所在的地址中。
*p = *padc;
*(p+1) = *(padc+1);
*(p+2) = *(padc+2);
此处我们默认按照正数处理,然后判断其符号。判断是不是负数只需要判断*(padc+2)的最高位是不是1即可,将其与0x80相与,如果结果为1则表示其为负数,反之则不是。即:
if(*(padc+2) & 0x80)
{
*(p+3) = 0xff;
return value;
}
else
{
return value;
}
假设我们现在有如下数据,下面对其一一进行检验。
原始数据(二进制) | 理论值(十进制) |
0X7FFFFF | 8388607 |
0X400000 | 4194304 |
0X000001 | 1 |
0X000000 | 0 |
0XFFFFFF | -1 |
0XC00000 | -4194304 |
0X800000 | -8388607 |
这里定义三个数组来存储我们的原始数据,LL为数据的低字节,MM是数据的中间字节,HH为数据的高字节。在测试的时候,给adc[ ]赋值,然后调用刚才编写的函数。最后将结果打印出来,跟预期的结果进行对比,就可以知道我们的函数有没有错误。
unsigned char LL[7] = {0xff,0x00,0x01,0x00,0xff,0x00,0x00};
unsigned char MM[7] = {0xff,0x00,0x00,0x00,0xff,0x00,0x00};
unsigned char HH[7] = {0x7f,0x40,0x00,0x00,0xff,0xc0,0x80};
然后我们定义一个for循环,依次取出其中的数据进行检验
int main( )
{
int i;
for(i = 0; i < 7; i++)
{
adc[0] = LL[i];
adc[1] = MM[i];
adc[2] = HH[i];
value = adc24to32(adc);
printf("\nThis 24bit value %02x%02x%02x change to int32 is %d\n",adc[2],adc[1],adc[0],value);
}
return 0;
}
程序运行的结果如下:
跟表格里的结果进行对比,可知程序没有错误。