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
#include
#include
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include
#include
#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 );
}
}