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 <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, &regData)){
		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, &regData)){
		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, &regData)){
		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, &regData)){
		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 );
    }
}

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值