• HAL库实现基于STM32+RN8302B的电压采集


    一、硬件电路

    这里以C相电压采集为例,来快速了解RN8302B的使用

    \\I_{R40}=220V/220k=1mA \\U_{R43}=1mA*250\Omega =250mV \\U_{R46}=U_{R43}=250mV \\(U_{R46}-U_{R52})/20k=(U_{R52}-U_{R57})/10k \\ \because U_{R52}=0 \\ \therefore V_{CP}=U_{R57}=-U_{R46}/2=-125mV

     

     二、 配置工作

    1、准备好printf工程,参考之前的教程

    STM32CubeIDE实现printf重定向输出到串口_飞由于度的博客-CSDN博客STM32CubeIDE之printf重定向及串口https://blog.csdn.net/qq_35629563/article/details/126665838

    2、配置RN8302B通信引脚

    ①SPI引脚

    具体原因参考《RN8302B 用户手册》:

    1)1.1 芯片特性

    2) 5.4 SPI 写时序

     CPOL表示SCK空闲时间的状态,从时序图里可以看出CPOL=LOW

    CPHA表示在第一边沿还是第二边沿进行数据交换,从时序图里可以看出CPOL=2 Edge

     ②控制引脚

    将PB12配置为GPIO_Output模式,PC6配置为GPIO_Input模式

     ③呼吸灯

    这里我是用的是PC1作为呼吸灯,其实不论使用哪个引脚控制的呼吸灯,只需将其重命名为LED1即可

    3、引脚重命名

    然后右击引脚分别重命名(注意顺序:先左击配置模式,再右击重命名)

    最终结果

    生成代码

    三、代码编写

    1、rn8302b.h

    1. #ifndef _SPI_H_
    2. #define _SPI_H_
    3. #include "main.h"
    4. #include "stdio.h"
    5. //---------------------- RN8302计量寄存器地址定义---------------------------------------------------//
    6. #define UC_CMD 0x0009//4//C相电压有效值
    7. #define UC_BYTE 4
    8. #define SYSSR_CMD 0x018A//系统状态寄存器
    9. #define SYSSR_BYTE 2
    10. #define DeviceID_CMD 0x018f//RN8302B Device ID
    11. #define DeviceID_BYTE 3
    12. #define SPI_CS_HIGH() HAL_GPIO_WritePin( RN_NSS_GPIO_Port , RN_NSS_Pin, GPIO_PIN_SET )
    13. #define SPI_CS_LOW() HAL_GPIO_WritePin( RN_NSS_GPIO_Port , RN_NSS_Pin, GPIO_PIN_RESET )
    14. void ReadAmmeterData(void);
    15. #endif

    2、rn8302b.c

    1. /****************SPI 采用端口模拟**********************************/
    2. #include "rn8302b.h"
    3. #include "spi.h"
    4. /*****************SPI 写单字节**************************/
    5. void Write_SPI_OneByte(uint8_t TxData)
    6. {
    7. uint8_t Rxdata;
    8. HAL_SPI_TransmitReceive(&hspi2,&TxData,&Rxdata,1, 1000);
    9. return ;
    10. }
    11. /*****************SPI 读单字节**************************/
    12. uint8_t Read_SPI_OneByte(void)
    13. {
    14. uint8_t Rxdata;
    15. uint8_t TxData=0;
    16. HAL_SPI_TransmitReceive(&hspi2,&TxData,&Rxdata,1, 1000);
    17. return Rxdata;
    18. }
    19. /************************* READ_RN8302*************************************************/
    20. uint32_t READ_SPI(uint16_t Address,uint8_t Data_len)
    21. {
    22. uint8_t i, DTemp,CheckSumR;
    23. uint32_t drData;
    24. SPI_CS_LOW(); // 开启SPI传输
    25. DTemp = (uint8_t)(Address&0x00FF);//ADDR[7:0]
    26. CheckSumR = DTemp;
    27. Write_SPI_OneByte(DTemp);
    28. DTemp = (((uint8_t)(Address >> 4)) & 0xf0) ;//CMD=R/W+AD[10:8]+BL[1:0]+2’h0(MSB+LSB)
    29. CheckSumR += DTemp;
    30. Write_SPI_OneByte(DTemp);
    31. HAL_Delay(5);
    32. drData = 0x00000000;//Read 24bit
    33. for(i=0;i//先发高字节,后发低字节
    34. {
    35. DTemp = Read_SPI_OneByte();
    36. drData = drData<<8;
    37. drData += DTemp;
    38. CheckSumR += DTemp;
    39. }
    40. CheckSumR = ~CheckSumR;//校验和算法: ADDR+CMD+DATA 单字节求和取反。
    41. if (CheckSumR != Read_SPI_OneByte())//比较校验和
    42. {
    43. drData = 0xFFFFFFFF;
    44. }
    45. SPI_CS_HIGH(); //关闭SPI传输
    46. HAL_Delay(2);
    47. return drData;
    48. }
    49. /**************************Write RN8302******************************/
    50. void Write_SPI(uint16_t Address, uint32_t dwData,uint8_t Date_len)
    51. {
    52. uint8_t i,CheckSumR, DTemp;
    53. SPI_CS_LOW(); //开启SPI传输
    54. DTemp = (uint8_t)(Address&0x00FF);//
    55. CheckSumR = DTemp;
    56. Write_SPI_OneByte(DTemp);//写地址
    57. DTemp = (((uint8_t)(Address >> 4)) & 0xf0)+0x80 ;
    58. CheckSumR += DTemp;
    59. Write_SPI_OneByte(DTemp);//写命令
    60. for (i =0 ;i < Date_len;i++)//先发高字节,后发低字节
    61. {
    62. DTemp = (uint8_t)(dwData>>(Date_len-1-i)*8);
    63. Write_SPI_OneByte(DTemp);//写命令
    64. CheckSumR += DTemp;
    65. }
    66. CheckSumR = ~CheckSumR;//校验和算法: ADDR+CMD+DATA 单字节求和取反。
    67. Write_SPI_OneByte(CheckSumR);//写命令
    68. SPI_CS_HIGH(); //结束传输
    69. HAL_Delay(2);
    70. }
    71. void ReadAmmeterData(void)
    72. {
    73. uint32_t temp = 0;
    74. temp = READ_SPI(DeviceID_CMD,DeviceID_BYTE);//芯片ID//supposed to be 0x00830200(hex)
    75. printf("Device_ID=%x(hex)\r\n",(int)temp);
    76. if(temp != 0x00830200) //读ID
    77. {
    78. printf("===================== \r\n");
    79. printf("Device_ID!=0x00830200 \r\n");
    80. printf("===================== \r\n");
    81. return;
    82. }
    83. temp = READ_SPI(SYSSR_CMD,SYSSR_BYTE);//系统状态
    84. printf("SYSSR=%x(hex)\r\n",(int)temp);
    85. HAL_Delay(1000);
    86. temp = READ_SPI(UC_CMD,UC_BYTE);//读取电压有效值
    87. printf(" UC 寄存器有效值=%8X\r\n",(int)temp);
    88. printf(" UC 引脚处有效值=%8.2f\r\n",(float)(temp/167772));
    89. printf(" UC 端口处有效值=%8.2f\r\n",(float)(temp/167772*1.76));
    90. }

     167772的来源

    RN8302B 用户手册》的3.2.2 有效值寄存器 提到

    1-4 类有效值是四字节寄存器,为 28 位( bit0-bit27 )有符号数,采用补码格式, bit27
    符号位, bit28-bit31 bit27 相同总为零。这四类有效值参数更新的周期为 250ms

     4.2.2 标准电压电流和有功功率值计算  提到

    U理论=INT[(Vu/800)*2^27]

    其中U理论是连接到RN8302B引脚上的电压值,Vu寄存器里的值(2^27/800=167772

    1.76的来源

    见本文《一、硬件电路》(1.76=220V/125mV=1.76)

    3、main.c

    USER CODE BEGIN Includes下添加以下代码

    1. #include
    2. #include
    3. #include "rn8302b.h"
    4. uint8_t t=0;

    USER CODE BEGIN 3下添加以下代码

    1. ReadAmmeterData();
    2. HAL_Delay(3000);
    3. HAL_GPIO_TogglePin(LED1_GPIO_Port, LED1_Pin);
    4. printf("%d\r\n",++t);

    四、实验结果

    全部代码与RN8302B的手册

    https://download.csdn.net/download/qq_35629563/86512618icon-default.png?t=N7T8https://download.csdn.net/download/qq_35629563/86512618

  • 相关阅读:
    CloudCompare 二次开发(9)——半径滤波
    基本排序算法的总结笔记
    十年老安卓开发转车载行业,成功斩下50K*13offer的历程经历
    如何使用 JavaScript/jQuery 为网站创建暗/亮模式?
    Drools规则引擎快速入门(一)
    React学习5(React class 组件)
    jvm YGC和FGC发生的具体场景
    蓝桥杯每日一题2023.10.22
    浅谈MySQL日志文件|手撕MySQL|对线面试官
    NSSCTF做题第9页(2)
  • 原文地址:https://blog.csdn.net/qq_35629563/article/details/126772062