BL0942是一颗内置时钟免校准电能计量芯片 SPI接口驱动
PCB原理图(220VN就是GND,因为空间大小有要求电源用的是非隔离降压芯片)
PCB布局 (就参考下,背面有块ESPC3芯片所以一些过孔看起来奇怪)
BL0942.h
#ifndef _BL0942_H #define _BL0942_H #include "driver/spi_master.h" //GPIO define #define BL0942_NSS_GPIO 10 // // Private function,这里是借用ra01s的库函数 // bool spi_write_byte(uint8_t* Dataout, size_t DataLength ); // bool spi_read_byte(uint8_t* Datain, uint8_t* Dataout, size_t DataLength ); // uint8_t spi_transfer(uint8_t address); bool bl_spi_write_byte(uint8_t* Dataout, size_t DataLength ); bool bl_spi_read_byte(uint8_t* Datain, uint8_t* Dataout, size_t DataLength ); // 初始化 esp_err_t BL0942_Init(void); // 功能描述:对BL0942的寄存器写 void BL09_Write_Reg(uint8_t addr, uint32_t temp); // 功能描述:对BL0942的寄存器读 uint8_t BL09_Read_Reg(uint8_t addr, uint32_t *data); // 复位 初始化寄存器 void BL09_Reset(void); /// @brief BLO942电表数据采集,这里用的是CT1=1000的电流互感器 /// @param data0 实际电压值 /// @param data1 实际电流值 /// @param data2 实际有功功率值 /// @param data3 用的电量 void BL09_Meter_Scan(float * data0, float * data1, float * data2, float * data3); #endif
BL0942.c
#include "BL0942.h" #include <stdio.h> #include <stdlib.h> #include <string.h> #include "freertos/FreeRTOS.h" #include "freertos/task.h" #include <driver/spi_master.h> #include <driver/gpio.h> #include "esp_log.h" #include "esp_err.h" #define R_I_RMS 0x03 //电流有效值寄存器,无符号 #define R_V_RMS 0x04 //电压有效值寄存器,无符号 #define R_WATT 0x06 //有功功率寄存器,有符号 #define R_CF_CNT 0x07 //有功电能脉冲计数寄存器,无符号 #define R_MODE 0x19 //用户模式选择寄存器 #define R_SOFT_RESET 0x1c //写入 0x5A5A5A 时,用户区寄存器复位 #define R_USR_WRPROT 0x1D //用户写保护设置寄存器 // SPI GPIO 引脚定义 #define PIN_NUM_MISO 4 #define PIN_NUM_MOSI 7 #define PIN_NUM_CLK 6 #define DEBUG_LOG false // 是否输出调试信息 volatile float Meter_Rece_Voltage = 0; // 实际电压值 volatile float Meter_Rece_Current = 0; // 实际电流值 volatile float Meter_Rece_Elec = 0; volatile float Actual_active_power_value = 0; // 实际有功功率值 volatile float electricity_used = 0; // 用的电量 spi_device_handle_t SPI_Handle; // SPI 处理句柄 static char * TAG = "BL0942"; esp_err_t BL0942_Init(void) { esp_err_t ret; ESP_LOGI(TAG, "Initializing bus SPI%d...", SPI2_HOST+1); spi_bus_config_t buscfg={ .miso_io_num = PIN_NUM_MISO, .mosi_io_num = PIN_NUM_MOSI, .sclk_io_num = PIN_NUM_CLK, .quadwp_io_num = -1, .quadhd_io_num = -1, .max_transfer_sz = 60*8, // 最大传输值 }; ret = spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO); // esp32 spi 接口初始化 ESP_ERROR_CHECK(ret); if(ret!=ESP_OK) ESP_LOGW(TAG, "Initializing esp32_spi_bus feil! \r\n"); //Attach the LCD to the SPI bus spi_device_interface_config_t devcfg; memset( &devcfg, 0, sizeof( spi_device_interface_config_t ) ); devcfg.clock_speed_hz = 400*1000; // 这个不能低于900KHz devcfg.spics_io_num = -1; devcfg.queue_size = 7; devcfg.mode = 0; devcfg.flags = SPI_DEVICE_NO_DUMMY; ret=spi_bus_add_device(SPI2_HOST, &devcfg, &SPI_Handle); ESP_ERROR_CHECK(ret); if(ret!=ESP_OK) ESP_LOGW(TAG, "Initializing esp32_spi_bus feil! \r\n"); // 复位 初始化寄存器 BL09_Reset(); // Test! // uint32_t readData = 0; // BL09_Read_Reg(0x18, &readData); // 读取的正常是 0x24 return ret; } // 延时 void Bl09_Delay(int data) { vTaskDelay( data / portTICK_PERIOD_MS ); } /******************************************************************************* 功能描述:对BL0942的寄存器写 输入参数: addr:寄存器地址 temp:寄存器值 *******************************************************************************/ void BL09_Write_Reg(uint8_t addr, uint32_t temp) { uint8_t sendData[6] = {0}; sendData[0] = 0XA8; //写操作识别字节 sendData[1] = addr; //地址 sendData[2] = (uint8_t)((temp&0x00ff0000)>>16); sendData[3] = (uint8_t)((temp&0x0000ff00)>>8); sendData[4] = (uint8_t)((temp&0x000000ff)); //检验和数据 sendData[5] = ((sendData[0]+sendData[1]+sendData[2]+sendData[3]+sendData[4]) & 0xff); sendData[5] = ~sendData[5]; // Test! // printf("BL09_Write_Reg: "); // for(int i = 0; i < 6; i++){ // printf(" 0x%x ", sendData[i]); // } // printf("\n"); //拉高CS gpio_set_level(BL0942_NSS_GPIO, 1); Bl09_Delay(10); gpio_set_level(BL0942_NSS_GPIO, 0); Bl09_Delay(5); bl_spi_write_byte(sendData, 6); Bl09_Delay(5); gpio_set_level(BL0942_NSS_GPIO, 1); } /******************************************************************************* 功能描述:对BL0942的寄存器读 输入参数: addr:寄存器地址 data: 返回值: 0:读取失败 1:读取成功 *******************************************************************************/ uint8_t BL09_Read_Reg(uint8_t addr, uint32_t *data) { uint8_t sendData[6] = {0}; uint8_t recvData[6] = {0}; uint8_t checkSum; sendData[0] = 0x58; //读操作识别字节 sendData[1] = addr; //寄存器地址 //拉高CS gpio_set_level(BL0942_NSS_GPIO, 1); Bl09_Delay(10); gpio_set_level(BL0942_NSS_GPIO, 0); Bl09_Delay(5); bl_spi_read_byte(recvData, sendData, 6); Bl09_Delay(5); gpio_set_level(BL0942_NSS_GPIO, 1); // Test! // printf("BL09_Read_Reg: "); // printf(" 0x%x ", sendData[0]); // printf(" 0x%x ", sendData[1]); // for(int i = 2; i < 6; i++){ // printf(" 0x%x ", recvData[i]); // } // printf("\n"); //校验和计算 checkSum = ((sendData[0]+sendData[1]+recvData[2]+recvData[3]+recvData[4]) & 0xff); checkSum = ~checkSum; if(checkSum != recvData[5]){ ESP_LOGW(TAG, " 校验值错误 \n"); return 0; } *data = (recvData[2]<<16) + (recvData[3]<<8) + recvData[4]; return 1; } //BL0942初始化 void BL09_Reset(void) { uint32_t writeData = 0; uint32_t readData = 0; writeData = 0x5a5a5a; //用户去寄存器复位,使用 BL09_Write_Reg(R_SOFT_RESET, writeData); Bl09_Delay(250);Bl09_Delay(250); while(1) { writeData = 0x55; //关闭写保护 BL09_Write_Reg(R_USR_WRPROT, writeData); writeData = 0; if(BL09_Read_Reg(R_MODE, &readData)) { writeData = (readData|(0x01<<6)); BL09_Write_Reg(R_MODE, writeData); } if(BL09_Read_Reg(R_MODE, &readData)) { if(readData == writeData) break; } ESP_LOGW(TAG, "error Init"); } writeData = 0xaa; //开启写保护 BL09_Write_Reg(R_USR_WRPROT, writeData); } /// @brief BLO942电表数据采集,这里用的是CT1=1000的电流互感器 /// @param data0 实际电压值 /// @param data1 实际电流值 /// @param data2 实际有功功率值 /// @param data3 用的电量 void BL09_Meter_Scan(float * data0, float * data1, float * data2, float * data3) { uint32_t regData; if(BL09_Read_Reg(R_V_RMS, ®Data)){ Meter_Rece_Voltage = (regData * 1.218 * (390*5 + 0.51)) / (73989 * 0.51 * 1000); *data0 = Meter_Rece_Voltage; if(DEBUG_LOG){ printf("\n****BL0942****:\n"); printf("实际电压值: %f V\n", Meter_Rece_Voltage); } } if(BL09_Read_Reg(R_I_RMS, ®Data)){ Meter_Rece_Current = (regData * 1.218) / (305978); *data1 = Meter_Rece_Current; if(DEBUG_LOG){ printf("实际电流值: %f A\n", Meter_Rece_Current); } } if(BL09_Read_Reg(R_WATT, ®Data)){ Actual_active_power_value = (regData * 1.483524 * (390*5 + 0.51)) / ( 3537 * 0.51 * 1000 ); *data2 = Actual_active_power_value; if(DEBUG_LOG){ printf("实际有功功率值: %f W\n", Actual_active_power_value); } } if(BL09_Read_Reg(R_CF_CNT, ®Data)){ electricity_used = regData * ( (1638.4 * 256 * 1.483524 * (390*5 + 0.51)) / ( 3600000 * 3537 * 0.51 * 1000 ) ); *data3 = electricity_used; if(DEBUG_LOG){ printf("用的电量: %f 度\n", electricity_used); } // Meter_Rece_Elec += tampElec; // printf("Meter_Rece_Elec: %f\n\n", Meter_Rece_Elec); } } bool bl_spi_write_byte(uint8_t* Dataout, size_t DataLength ) { spi_transaction_t SPITransaction; if ( DataLength > 0 ) { memset( &SPITransaction, 0, sizeof( spi_transaction_t ) ); SPITransaction.length = DataLength * 8; SPITransaction.tx_buffer = Dataout; SPITransaction.rx_buffer = NULL; spi_device_transmit( SPI_Handle, &SPITransaction ); } return true; } bool bl_spi_read_byte(uint8_t* Datain, uint8_t* Dataout, size_t DataLength ) { spi_transaction_t SPITransaction; if ( DataLength > 0 ) { memset( &SPITransaction, 0, sizeof( spi_transaction_t ) ); SPITransaction.length = DataLength * 8; SPITransaction.tx_buffer = Dataout; SPITransaction.rx_buffer = Datain; spi_device_transmit( SPI_Handle, &SPITransaction ); } return true; }
使用示例 (小白的话不要照搬哈,好好看看)
#include "BL0942.h" void app_main(void) { // GPIO Init gpio_set_level(RELAY_C_PIN, 1); // 继电器关 gpio_set_level(LED_PIN, 1); // LED灭 gpio_set_level(BL0942_NSS_GPIO, 1); gpio_set_level(3, 1); gpio_set_direction(3, GPIO_MODE_OUTPUT); // Lora gpio_set_direction(RELAY_C_PIN, GPIO_MODE_INPUT_OUTPUT); // 继电器 gpio_set_direction(BL0942_NSS_GPIO, GPIO_MODE_OUTPUT); // 计量的NSS位选 gpio_set_direction(LED_PIN, GPIO_MODE_INPUT_OUTPUT); // LED gpio_set_direction(BUTTON_PIN, GPIO_MODE_INPUT); // 按键 gpio_set_level(3, 1); gpio_set_level(RELAY_C_PIN, 1); // 继电器关 gpio_set_level(BL0942_NSS_GPIO, 1); gpio_set_level(LED_PIN, 1); // LED灭 BL0942_Init(); } float Rece_Voltage = 0; // 实际电压值 float Rece_Current = 0; // 实际电流值 float active_power_value = 0; // 实际有功功率值 float elect_used = 0; // 用的电量 void Deal_Task(void * art) { for (;;) { BL09_Meter_Scan(&Rece_Voltage, &Rece_Current, &active_power_value, &elect_used); vTaskDelay( 500 / portTICK_PERIOD_MS ); } }