当前位置: 网站首页 > 技术应用 > 电能计量芯片应用

BL0942 ESP32-C3 IDF4.4 电能计量芯片 开发笔记

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 );
    }
}
 
点击次数:  【关闭
  • 壹零零单片机©版权所有 2008-2025 粤ICP备17151077号

Powered by  xinfuke  5.2.5 ©2008-2025  www.100mcu.com