HT7036数据读取(补码原码转换)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
HT7036数据读取(补码原码转换)
- 前言
- 一、原码?补码?
- 二、案发现场
- 二、此地无坑,还是跳了下去
- 三、补码转原码
- 四、最初的起点
- 总结
前言
虽然在单片机的数据处理中补码使用的场合很少,但是在使用一些外部模块的时候,由于模块产生的数据是补码类型的,这就不可避免的让脑袋不大灵光的我又跳进了一个深坑,此篇文章,以示警醒。
提示:以下是本篇文章正文内容,下面案例可供参考
一、原码?补码?
至于源码,反码,补码的转换,论坛上有很多的讲解,我就不重复造轮子了(其实我也不太懂,能解决实际问题就行)。关于原码反码补码之间的转换,可以参考以下文章:
篇幅很短,但通俗易懂
其中这句话很精髓。
二、案发现场
还原案发现场:两天前,某佩奇由于实验需要,玩起了HT7036,这是一款三相电能计量芯片,相比于HLW8032功能强大了不少,在佩奇苦苦的哀求下,实验室大佬佐助将HT7036芯片的初始化源码和用户参考手册发给了我,怀着感激的心,立刻研究起了芯片。
芯片的具体功能还没有细究,毕竟,先把时序写出来,随便读取一个寄存器的值,能看到希望才有动力进行下一步嘛。需要注意的是,HT7036的初始化程序使用的模拟SPI,其中读写数据时涉及到延时,在使用STM32F103RCT6作为主控芯片时,只需要将例程中的所有延时函数改成delay_us(1);即可,起初想过用__nop()来替代延时函数,如下图:
理解为等待5个系统时钟周期,想通过这样的方法来提高模拟SPI的传输速度(毕竟例程中也是这样的嘛),测试如下:
其中r_UaRms,r_UbRms,r_UcRms对应的寄存器地址如下:
通过读取寄存器的值,将读取到的数据打印到串口助手,测试结果如下图:
看到这里时,我激动到飞起,能读到ID寄存器的默认值,就是图中的0x7022e0,倒是仔细往下看时,发现读出来的电压有效值全部为零??是没有接三相电的原因??于是就借助了实验室的仪器,给其中一路采集通道加上了交流电,但还全部都是0!!!,这就不像话了,明明读出来的ID是对的,说明模拟SPI的读写时序是没有问题的,怎么会读出来的全部都是0呢???没办法,只能用示波器一点一点的找问题(此处无图),最后发现还是时序的问题,当把延时函数改为如下时:
它就成了,结果如下图所示:
通过改变交流电压,数值也在有规律的变化,这下前期工作也算差不多完成了,更大的坑还在后面等着我。
二、此地无坑,还是跳了下去
也许是由于受到例程的影响,我也理所当然的直接读取了对应的寄存器,将寄存器中的值读取了出来,按照道理来说,输入的波形是一个正弦波,那么无论采集的速度快慢,采集到的数据应该也是正弦波才对,但是,我还是不得不佩服我的脑回路(或者说佩服我的眼睛),代码如下,其中r_SampleUC就是上图中对应的0x34
测试结果如下:
这是正弦波???有点像梯形波对不对??也有点正弦波的味道??行了行了,别再欺骗自己了,开始也想过,产生这种情况的原因也推测过,可能是读取数据的方式不对,事已至此,就换个方法测量数据吧,HT7036中有一个寄存器,其介绍如下:
在使用缓存数组之前,需要开启缓存命令,选择需要缓存数据的通道,
之后对数据进行缓。0xc1命令可以获得当前缓存了多少数据,代码如下图:
在这里选择了Uc的通道进行采集缓存,延时100毫秒才能采集完1024个数据(50毫秒能采集700个数据),缓存之后进行读取打印,结果如下:
这TM是什么妖魔鬼怪???方天画戟???由于数据转换的原因,16进制的数据转化为整形导致的??那我们就再来看看原原本本的16进制数据:
红色斜线为分界线数据从最大以下跳到最小,和波形图打印出的结果基本一样,也看不出来什么结果,还是回去老老实实看波形吧。其实仔细观察波形,会发现一个很有趣的事情,把上面的波形放大,仔细观察:
有没有一点正弦波的后半个周期的波形样子???这完完全全就是啊!!再回到之前那个方天画戟的波形仔细观察,将顶端和底端连结起来,新大陆!!!采集出来的就是正弦波!!但是由于一些原因数据发生了跳变,并且跳变的幅度很大。那这么说,之前采集的实时值是不是也是这样的波形??只不过速度太快了导致出来的是梯形波??回去仔细观察下
(发现CSDN不能上传视频,只能插入GIF,但是又特别麻烦,就用图片展示):
实际将y轴拉长就会出现一个半波,这说明,采集的数据是正确的,可以开始下一步,再跳进一个更大的坑…
三、补码转原码
产生上述结果的原因,其实在用户手册中已经提及到了,而且提及到很多次,但我就是知道有坑还是往下跳,先上用户手册:
结合着这句话就很清除的明白了,产生跳变的原因是什么了,由于单片机读回来的是补码数据,最高位位符号位,转换为源码需要除符号位取反再加1才能得到原码(此时的原码最高位是符号位,1为负,0为正),不懂得可以看开篇的链接或者查找相关的书籍。
话说,到这一步,应该能解决问题了吧,正数的补码转化为原码不用转,直接使用就可以。负数的补码在代码中实现就是全部取反 然后加1,再在前面加个符号,就是负数的原码其实这样也没有错,也是按照定义来的,但是后面还又一个坑,细心的人到这里就能采集到正弦波了,奈何我粗心大意,还要再写一个小结来解决这个问题。
以下补码转原码的代码只适用于当前的寄存器,代码如下:
int Complement_2_Original(u32 Comp_data)
{int Orig_data;if(Comp_data & 0x40000){Orig_data = (~Comp_data) + 1;Orig_data = -Orig_data;}else{Orig_data = Comp_data;}return Orig_data;}
四、最初的起点
仔细检查上面的代码,根据补码转原码的定义来的(还有溢出的情况没考虑,暂时还没完善),正常使用应该是没有问题的,那就把它带到程序中进行测试:
代码如下:
结果如下:
纳尼???怎么还是这种情况???逻辑上来说,应该是很标准的正弦波啊!!!又出问题了啊,没办法,开始排查错误,既然传回来的数据没有问题,那问题可能就出在转换函数上面(因为这方天画戟波也没怎么变化啊)。那我们就单独的验证这个函数,打开祖传的VC++6.0,将转换函数复制过去。
数据被原封不动的打印出来了。也就是说,在第18个bit位为1的时候,没有进入到if()语句,而是进入了else()语句,这问题就大了,祖传的VC++6.0出问题了??按位运算都不会了??如果这样想,这个问题就解决不了了。我们再进行下一步的测试,在语句块中加上打印语句,来判断是进入了哪个语句块:
是不是更加奇怪了,明明进入了if()语句,但是结果没变??那肯定是在转换的时候出问题了。先来手推一遍正确的结果:
1111 1111 0101 0100 1011 1000 //0xff54b8对应的二进制码由于数据是18位的,即0~17bit,第19位为符号位,即18bit,高于18bit的位可以忽略将上述补码除符号位其他位都按位取反,结果如下0000 0100 1010 1011 0100 0111,高五位可以忽略,置1清0无所谓(0000 0100 1010 1011 0100 0111)+1,取反后加10000 0100 1010 1011 0100 1000,这个就是原码,为负数
将上述二进制的原码转换为十进制,为 -43848,这和打印出来的0xff54b8对应的十进制 16733368 差了不知多少倍,知道了正确结果,开始在负数的补码转换原码语句块中一步一步调试。首先将取反后加1的结果以2进制打印出来。
怎么还是这么大的数字??先别着急,将上面的0xff00ab48转换为二进制
1111 1111 0000 0000 1010 1011 0100 1000 (0xff00ab48)0000 0100 1010 1011 0100 1000 (0xff54b8除符号位取反+1,右对齐)
有没有发现有一点相似,不只是相似,是相同,也就是说,对补码取反后加1的结果是正确的,现在的问题就是,高8bit的1是怎么来的,通过函数的形参类型可以知道,函数传进来的是 unsigned int类型的,占用4个字节,虽然int类型得1也占用4个字节,但是,int类型有正负号,还是以0xff54b8为例:
当把形参传进去得时候,0xff54b8为
0000 0000 1111 1111 0101 0100 1011 1000
这是一个无符的int类型,占4个字节(32位),当进行取反操作(注意符号位)的时候,转换结果如下
1111 1111 0000 0100 1010 1011 0100 0111
然后加1
1111 1111 0000 0100 1010 1011 0100 1000
和上面的值特别大结果一模一样是不是
这样我们就知道高8bit的1是怎么来的了,下一步操作,就是取我们想要得数据了呀,直接与上0x3ffff,结果如下:
是我们想要的结果,由于在代码中直接对补码全部取反了(包括符号位),所以在取出我们想要的数据之后,再在前面添加一个符号,就完成了。再来看以下结果:
和之前手推的结果一模一样。现在把这个函数代入到程序中去,打印出波形:
大功告成,从来没发现正弦波如此的好看!
总结
改掉进去不该掉进去的坑算是一个都逃不过。
HT7036数据读取(补码原码转换)相关推荐
- 补码原码转换c语言代码,c/c++原码反码补码原理 以及进制转换
1首先了解 低字节位 和高字节位 看图 2字节在内存的排列方式//int num = 010; //0开头代表 8禁止 //("%d",num); //所以打印的是8 int nu ...
- 补码原码反码溢出问题
数据的机器层次表示 文章目录 数据的机器层次表示 2.1 补码+原码+反码 1. 原码表示法 2. 补码表示法 3. 反码表示法 4. 三种表示法比较 2.2 原码补码加减法运算 1. 补码加法 2. ...
- 补码原码反码简单理解
补码原码反码简单理解 看到一句I2S的data是以补码形式来表现数据.再次查阅得出以下只管结论: ####1,首先直观举例机器里面是如何存放有符号数的:#### 1在机器里面表示为 0000 0001 ...
- 负数的补码和原码转换
负数的补码和原码双向转换过程是相同的,都是逐位 求非 再加 1 . 同一个值的正负数是相反的值,所以要求非: 求非之后再加1则是因为中间的"零"占了一个数的位置:
- java反码补码原码作用_java原码补码反码关系解析
本文为大家解析了java原码补码反码的关系,供大家参考,具体内容如下 原码:不管源数据是十进制还是十六进制,统统将数字转成二进制形式 反码:把原码的二进制统统反过来,0变成1,1变成0 补码:负数的反 ...
- 补码 原码 反码
假设有一个 int 类型的数,值为5,那么,我们知道它在计算机中表示为: 00000000 00000000 00000000 00000101 5转换成二制是101,不过int类型的数占用4字节(3 ...
- 负数 补码 原码
负数在计算机中是用补码的形式存储的,正数在计算机中是用原码的形式存储的. 正数求原码直接将十进制转二进制即可,负数的补码是在原码的基础上除符号位外其余位取反后+1. 但是用这种方式求负数补码用编程实现 ...
- 计算机原理原码反码,计算机原理 计算 —11011011补码( ) 原码( ) 反码( )11001010补码 原码 反码...
-11011011补码( 100100101 ) 原码(111011011 ) 反码(100100100 ) 11001010补码 11001010 原码 11001010 反码 11001010 ( ...
- 从补码的来源剖析到为啥补码=原码按位取反+1
1.引入问题 你已经困惑了很久,你明明知道 补码就是按位取反,然后加一,但是你想知道的,不是它怎么求滴,而是,它怎来滴.当然,对于阅读这篇文章的你,既然想要知道这个答案,一定是有一定编程基础的读者,肯 ...
最新文章
- Centos6.8防火墙配置
- lua 实现策划需要保留的小数位数
- Java——String类中的compareTo方法总结
- 算法系列15天速成——第三天 七大经典排序【下】
- Jupyter notebook中用python matplotlib ax3.plot_surface绘制的三维图(3D图)(三维函数)无法旋转解决办法(%matplotlib notebook)
- 给其他账户访问mysql的权限,将postgresql数据库的权限授予其他用户
- 分布式事物一致性设计思路
- js 操作Listbox js 获取Listbox选择的值的代码
- java笔试完一般多久给通知_恭喜浙江,喜提浙江中烟招聘通知,一、二批共计招录130人左右...
- CSS word-wrap强制换行截断长字符串
- 求矩形的最小值c语言,C语言复习---矩形法求定积分函数
- Go map 转 slice
- 比亚迪汉鸿蒙系统测评_深度:预判比亚迪汉EV电驱动系统技术状态
- mount: 未知的文件系统类型“vboxsf”_好程序员云计算学习路线教程大纲课件:Mount 挂载详解...
- 三菱四节传送带梯形图_PLC控制四节传送带设计
- 刘汝佳小白书-最长回文字串
- RAID-6技术详解
- oneDrive登陆界面空白 的解决办法
- vb访问服务器文件,VB6打开远程服务器文件
- Java多线程实现简易微信发红包