位操作 寄存器 数据正负表示

下图是Sensor 的datasheet. 按我的理解这是 24位ADC (bit0 - bit23) 其中 bit21,22,23 是符号位,bit0 到 bit20是数据位。
stm32F4 板子
官网给的sample code 里面,在表示负数的adc值 没看明白,


bit21-23 符号位有4种情况 ,
(000) 正数 并没有超出正的最大值
(111) 负数 并没有超出负数的最小值
(001)正数但超出了正的最大值
(110)负数但超出了负数的最小值
故用一个int32的变量 adc_data 来表示 adc 的值
int32_t  adc_data = 0;

adc_data = (int32_t)(((SensorData[0] << 16) | (SensorData[1] << 8) | (SensorData[2] << 0))&0xFFFFFF);
 uint8_t sign = 0;
 sign =(uin8_t) ((adc_data & 0xE00000) >> 21);   //1110 0000, 0000 0000, 0000 0000

                                adc_data = (adc_data & 0x1FFFFF); //0001 1111, 1111 1111, 1111 1111

                                switch (sign)
                                {

                                    case 0: // Positive and lower than positive full-scale (within full-scale range)
                                        {
                                            adc_data = (adc_data & 0x1FFFFF);   //0001 1111, 1111 1111, 1111 1111
                                            break;
                                        }
                                    case 7: // Negative and higher than negative full-scale (within full-scale range)
                                        {
                                            adc_data = (adc_data & 0x1FFFFF) * (-1);// 我认为应该这样
                                            adc_data = (adc_data | 0xFFE00000); //官网给的代码 不理解
                                            break;
                                        }

                                    case 1:  //Out of Positive Full scale
                                        {
                                            adc_data = 0x1FFFFF; //0001 1111, 1111 1111, 1111 1111
                                            break;
                                        }

                                    case 6: // Out of Negative Full scale 
                                        {
                                             adc_data = 0x1FFFFF * (-1); // 我认为应该这样
                                             adc_data = 0xFFE00000; //  官网给的代码 不理解而且0xFFE00000 = 4,292,870,144 已经超出  int32 表示范围
                                            break;
                                        }

                                    default:
                                        {
                                            break;
                                        }
                                }// END switch


img

  1. 按照他文档的说法,如果是正数就直接输出,如果是负数,是按照补码输出的。文档中最小负分度值的表示就是111111...1111(一共24个1),这就是正数1的补码(补码就是将一个数字逐位取反然后再加1)。
  2. 在确认了补码的情况下,一般地,stm32编译器会把int32_t认为是占32个bit位的变量,而现在直接读取ADC结果的时候只会返回24位数值(其中高3位表示符号和是否超出表示范围),如果ADC的读出结果是一个正数,那么只需要将符号位截去,保留低21位就可以了,代码里的表现就是

adc_data = (adc_data & 0x1fffff)
  1. 而对于负数而言,还是拿最小负分度值举例子,你读到的是24个1,这个数字如果赋值给一个int32类型的变量,原来的高3位符号位就会变成有效数值,这不是我们想要的,而原来24个1对应到32位里,应该是32个1才对。所以实现上应该用读出的数据或上0xffe00000
  2. int32_t这个变量能表示的范围是“-2147483648”到“2147483647”,只看正数那边的话,16进制是0x00000000i到0x7fffffff
    纯手打,望采纳,可以迭代提问 :)
该回答引用于gpt与OKX安生共同编写:
  • 该回答引用于gpt与OKX安生共同编写:

这段代码是将从Sensor中读取到的24位ADC数据转换为一个32位有符号整型数据,并且将其进行了一些必要的处理以表示正负数和溢出情况。

具体来说,先将3个字节的数据(SensorData[0]、SensorData[1]、SensorData[2])按位拼接成一个24位的数据,然后再将该数据转换为32位有符号整型(int32_t)。在转换时,首先使用位与运算将数据中的高8位(即符号位)清零,然后根据符号位进行进一步的处理。

当符号位为000时,表示数据为正数并且没有超出正数的最大值,此时直接保留低21位的数据即可。

当符号位为111时,表示数据为负数并且没有超出负数的最小值,此时需要将低21位的数据取反,并且将整个数据加上0xFFE00000,以表示负数。注意,在这种情况下,低21位已经被取反,所以不能直接用乘法得到相反数。

当符号位为001时,表示数据为正数但超出了正数的最大值,此时将整个数据赋值为0x1FFFFF即可。

当符号位为110时,表示数据为负数但超出了负数的最小值,此时需要将整个数据赋值为0xFFE00000,以表示负数。注意,在这种情况下,低21位无法确定具体数值,因此将整个数据都改为0xFFE00000。

这种表示方法在STM32的官方文档中被称为“二进制补码”,常见于单片机或嵌入式系统中的数据表示。

参考GPT:这段代码的目的是将从传感器读取的 24 位 ADC 值转换为一个有符号的整数。下面对代码中的每一部分进行解释:

1 读取 ADC 值

adc_data = (int32_t)(((SensorData[0] << 16) | (SensorData[1] << 8) | (SensorData[2] << 0))&0xFFFFFF);

这一行代码将从传感器读取的 24 位 ADC 值合并为一个 32 位整数,并清除了最高 8 位,即保留了 ADC 值的最低 24 位。

2 确定 ADC 值的符号

sign =(uin8_t) ((adc_data & 0xE00000) >> 21);

这一行代码用位操作提取 ADC 值的符号位,即将 ADC 值的最高 3 位取出并右移 21 位,得到一个 0~7 之间的数字,代表 ADC 值的符号。

3 根据符号位进行转换
接下来的 switch 语句根据符号位的不同,对 ADC 值进行不同的转换。具体的转换过程如下:

符号位为 0:表示 ADC 值为正数且在正的最大值范围内,直接保留 ADC 值。
符号位为 7:表示 ADC 值为负数且在负的最小值范围内,先将 ADC 值乘以 -1 取反,然后将最高 11 位设置为 1,以得到一个有符号的 32 位整数。
符号位为 1:表示 ADC 值超出了正的最大值范围,将 ADC 值设置为最大的正值 0x1FFFFF。
符号位为 6:表示 ADC 值超出了负的最小值范围,先将 ADC 值乘以 -1 取反,然后将 ADC 值设置为最小的负值 0xFFE00000。
需要注意的是,符号位为 7 和 6 时,需要先将 ADC 值乘以 -1 取反。

在stm32F4板子上表示负数的ADC值。根据ST官方文档1,在stm32F4板子上,ADC采集到的数据是无符号的,因此需要将其转换为有符号的数据。具体来说,如果ADC采集到的数据是24位,则需要将其转换为32位有符号整数。转换方法如下:

将采集到的24位数据左移8位。
检查左移后的数据的最高位(即第31位),如果为1,则说明该数是负数。
如果该数是负数,则需要将其减去2^24。

打开windows计算器,
点击菜单切换成程序员模式
点击QWORD切换成DWORD
依次点击0,-,1,=
看HEX的值
理解清楚计算机怎么存储和表示负数的

不知道你这个问题是否已经解决, 如果还没有解决的话:
  • 关于该问题,我找了一篇非常好的博客,你可以看看是否有帮助,链接:STM32F4系列ADC通道之——ADC复用通道、规则通道和注入通道
  • 除此之外, 这篇博客: STM32F4HAL库常用函数总结(部分)中的 6.ADC的状态获取 部分也许能够解决你的问题, 你可以仔细阅读以下内容或者直接跳转源博客中阅读:
    HAL_StatusTypeDef HAL_ADC_Start_IT(ADC_HandleTypeDef* hadc);
    HAL_StatusTypeDef HAL_ADC_Stop_IT(ADC_HandleTypeDef* hadc);
    

    后续会进一步的更新,刚刚学习STM32F4板子,有错误欢迎大家指正。
    2020年9月1日 天气晴


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^