【开发杂谈】ESP32的连续ADC的结果转化 和 C语言中的数据类型对齐

在某个项目开发的过程中,我遇到了一个略微神经的情况:该项目主要使用ESP32-S3作为边缘节点,ESP32-S3需要使用ADC批量采集数据, 通过DMA交由CPU核心处理。

首先,ESP32-S3的ADC最大位宽为12位,也就是范围为0~4095。

其次,乐鑫的ESP-IDF中的ADC DMA数据采集的结构体如下:

typedef struct {
    union {
        struct {
            uint32_t data:          12; /*!<ADC real output data info. Resolution: 12 bit. */
            uint32_t reserved12:    1;  /*!<Reserved12. */
            uint32_t channel:       4;  /*!<ADC channel index info.
                                            If (channel < ADC_CHANNEL_MAX), The data is valid.
                                            If (channel > ADC_CHANNEL_MAX), The data is invalid. */
            uint32_t unit:          1;  /*!<ADC unit index info. 0: ADC1; 1: ADC2.  */
            uint32_t reserved17_31: 14; /*!<Reserved17. */
        } type2;                        /*!<When the configured output format is 12bit. */
        uint32_t val;                   /*!<Raw data value */
    };
} adc_digi_output_data_t;

可以看到,其中负责存储数据的data成员变量为32位的uint32_t。uint32_t一共有4个字节,ESP32会将12位的ADC结果作如下处理:0-7位写入第一个字节中,而8-11位则写入第二个字节中,第二个字节只用了4个位,剩余的会填充些其他数字,具体根据什么填充我还没搞懂。第三第四个字节则直接为全0。听起来很抽象,直接看一波实例:
在uint32_t中0值应该是00 00 00 00四个全零的字节, 如果ADC读出的值为2006,那么他的16进制为0x7D6,根据上述规则,0-7位写入第一个字节中,8-11位写入第二个字节的低4位,高四位随机填充为0x6,则实际uint32_t的值是0x00 00 67 D6,在内存中uint32_t的存储形式是

D6 67 00 00

可以看到0xD6被写入在了第一个字节中,0x7则被写入第二个字节的低4位中,而第二个字节的高四位则被填充了0x6。一般的用户知道数据被存进了uint32_t的data成员变量中,想当然地去取data值,但是此时data=0x67 D6,和0x7D6相差甚远。

网上的一个解决办法是这样的(下面有更简便解法,可直接跳转):

int8_t *buffer = adc_digi_output_data_t->data;
int xVal = (((uint16_t)buffer[1] & 0x0F) << 8) | buffer[0];

首先是将uint32_t转化为一个int8_t的数组,则就变成了一个有4个元素的数组buffer = [0xD6][0x67][0x00][0x00]。这里转化为数组的顺序是按照内存存储的顺序读取的,因此低8位反而在0号元素

然后我们来拆解int xVal = (((uint16_t)buffer[1] & 0x0F) << 8) | buffer[0];这条式子:

  1. (uint16_t)buffer[1] & 0x0F:先取出buffer[1]的值0x67 和 0x0F作与运算,可以消除掉其高四位的值,运算后的=0x07,然后转化为uint16_t,具体表现为高位补充8个0,变成0x00 07
  2. 将0x00 07左移4位,得到0x07 00
  3. 0x07 00和buffer[0]=0xD6作与运算,得到0x07 D6,等于十进制的2006,赋值给int型的xVal。这里可以不用将xVal设置为uint是因为int的最高位是符号位,但是我们运算过程中最高位一直都是0,所以是不是无符号数都可以

当然,我寻思了一会,更方便的解法应该是:
直接使用与运算,仅保留低12位,也就是int xVal = data & 0x0000 0FFF。这样对于uint32_t的数据位来说,会将12-31位置为0,保留0-11位原来的值,那么以0x0000 67D6为例子,则int xVal = 0x0000 67D6& 0x0000 0FFF = 0x0000 07D6= 2006,和ADC值一致

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值