一、系统简介
交流电控制系统是一种用于管理和保护交流电设备的系统。它主要用于监测和控制电流、电压、功率和温度等参数,以确保电气设备的安全运行。
过流保护是交流电控制系统的重要功能之一。当电流超过设定的安全阈值时,系统会自动切断电源,以防止电路过载和损坏。
过压保护是另一个关键功能,它监测电压是否超过设定的上限。如果电压异常升高,系统会采取措施,如切断电源或降低电压,以保护设备免受损害。
过功率保护用于控制电气设备的功率消耗。当功率超过设定范围时,系统会自动调整电流或降低负载,以避免设备过热或过载。
超温保护是为了防止设备过热而采取的预防措施。系统会监测设备的温度,并在达到危险温度时采取措施,如降低电流、增加散热或切断电源,以保护设备安全运行。
交流电控制系统的设计和应用广泛用于工业、商业和家庭领域,它能够提高电气设备的安全性和可靠性,减少损坏和事故的风险。使用这些保护措施可以有效保护设备,并确保电力系统的稳定和可持续运行。
二、控制设计
这个设计基于HLW8032电能采样芯片和温度采集芯片DS18B20,结合LCD1602显示屏。它还包括一个用于保护用电器的自动断电功能。在过温、过功率、过压或过电流情况下,系统会自动触发继电器断开电路,以保护连接的用电器。
HLW8032电能采样芯片通过实时测量电能数据提供精确的电能监测功能。DS18B20温度采集芯片用于采集环境温度数据,具有高精度和数字输出的特点。它们的数据会在LCD1602显示屏上以易读的方式显示出来。
为了保护用电器,该设计通过监测温度、功率、电压和电流等参数来实现自动断电功能。当温度超过设定阈值、功率超过额定值、电压超过安全范围或电流超过额定电流时,系统会立即触发继电器,切断电路,保护用电器免受潜在的危害。
这种自动断电功能使得该设计在安全性方面具有优势,可避免电器过载、过热、过电压或过电流引起的潜在风险。用户可以通过LCD1602显示屏实时监测电能消耗和环境温度,并在需要时采取必要的措施。
总结而言,这个设计结合了HLW8032电能采样芯片、温度采集芯片DS18B20和LCD1602显示屏,提供了电能监测、温度监测和自动断电保护功能。它适用于各种应用场景,为用户提供了安全可靠的用电环境。
三、仿真图设计![](https://img-blog.csdnimg.cn/direct/806ff2c8c360438e95c2e716b64555ba.png)
四、程序设计
1.DS18B20驱动程序
//设置DS18B20引脚为输出
void DS18B20_IO_OUT(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
//设置DS18B20引脚为输入
void DS18B20_IO_IN(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA, &GPIO_InitStructure);
}
//复位DS18B20
void DS18B20_Rst(void)
{
DS18B20_IO_OUT(); //SET PG11 OUTPUT
DS18B20_DQ_OUT = 0; //拉低DQ
delay_us(750); //拉低750us
DS18B20_DQ_OUT = 1; //DQ=1
delay_us(15); //15US
}
//等待DS18B20的回应
//返回1:未检测到DS18B20的存在
//返回0:存在
u8 DS18B20_Check(void)
{
u8 retry = 0;
DS18B20_IO_IN();//SET PG11 INPUT
while(DS18B20_DQ_IN && retry < 200)
{
retry++;
delay_us(1);
};
if(retry >= 200)
{
return 1;
}
else
{
retry = 0;
}
while(!DS18B20_DQ_IN && retry < 240)
{
retry++;
delay_us(1);
};
if(retry >= 240)
{
return 1;
}
return 0;
}
//从DS18B20读取一个位
//返回值:1/0
u8 DS18B20_Read_Bit(void) // read one bit
{
u8 data;
DS18B20_IO_OUT();//SET PG11 OUTPUT
DS18B20_DQ_OUT = 0;
delay_us(2);
DS18B20_DQ_OUT = 1;
DS18B20_IO_IN();//SET PG11 INPUT
delay_us(12);
if(DS18B20_DQ_IN)
{
data = 1;
}
else
{
data = 0;
}
delay_us(50);
return data;
}
//从DS18B20读取一个字节
//返回值:读到的数据
u8 DS18B20_Read_Byte(void) // read one byte
{
u8 i, j, dat;
dat = 0;
for(i = 1; i <= 8; i++)
{
j = DS18B20_Read_Bit();
dat = (j << 7) | (dat >> 1);
}
return dat;
}
//写一个字节到DS18B20
//dat:要写入的字节
void DS18B20_Write_Byte(u8 dat)
{
u8 j;
u8 testb;
DS18B20_IO_OUT();//SET PG11 OUTPUT;
for(j = 1; j <= 8; j++)
{
testb = dat & 0x01;
dat = dat >> 1;
if(testb)
{
DS18B20_DQ_OUT = 0; // Write 1
delay_us(2);
DS18B20_DQ_OUT = 1;
delay_us(60);
}
else
{
DS18B20_DQ_OUT = 0; // Write 0
delay_us(60);
DS18B20_DQ_OUT = 1;
delay_us(2);
}
}
}
//开始温度转换
void DS18B20_Start(void)// ds1820 start convert
{
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc);// skip rom
DS18B20_Write_Byte(0x44);// convert
}
//初始化DS18B20的IO口 DQ 同时检测DS的存在
//返回1:不存在
//返回0:存在
u8 DS18B20_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能PORTA口时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; //PORTA12推挽输出
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_SetBits(GPIOA, GPIO_Pin_12); //输出1
DS18B20_Rst();
return DS18B20_Check();
}
//从ds18b20得到温度值
//精度:0.1C
//返回值:温度值 (-550~1250)
float DS18B20_Get_Temp(void)
{
u16 temp = 0;
u8 a = 0, b = 0;
float value = 0;
DS18B20_Start(); // ds1820 start convert
DS18B20_Rst();
DS18B20_Check();
DS18B20_Write_Byte(0xcc);// skip rom
DS18B20_Write_Byte(0xbe);// convert
a = DS18B20_Read_Byte(); // LSB
b = DS18B20_Read_Byte(); // MSB
temp = b;
temp = (temp << 8) + a;
if((temp & 0xf800) == 0xf800)
{
temp = (~temp) + 1;
value = temp * (-0.0625);
}
else
{
value = temp * 0.0625;
}
return value;
}
2.STM32串口DMA
为什么使用串口DMA?
STM32使用串口DMA(直接内存访问)与HLW8032通信有以下好处:
提高通信效率:使用DMA进行串口通信可以实现数据的直接传输,无需CPU的干预。DMA控制器可以在背后独立地处理数据传输,从而释放CPU的负担。这样可以提高通信效率,减少了数据传输的延迟。
节约CPU资源:由于DMA控制器负责串口数据传输,CPU可以在数据传输期间执行其他任务。这将节约CPU的资源,使其能够同时处理其他重要的任务,提高了系统的整体性能和响应能力。
减少中断次数:传统的串口通信方式通常需要依赖中断来处理每个接收或发送的数据字节。而使用DMA后,可以批量传输数据,减少了中断次数,降低了系统的中断负荷。
提高系统可靠性:DMA传输是硬件控制的,相对于软件控制的方式更可靠。它可以提供更稳定、更可靠的数据传输,减少了数据丢失或错误的可能性。
简化编程逻辑:使用DMA进行串口通信可以简化编程逻辑。DMA控制器的配置相对简单,一旦配置完成后,数据传输可以自动进行,无需频繁地编写中断服务程序。
综上所述,STM32使用串口DMA与HLW8032通信具有提高通信效率、节约CPU资源、减少中断次数、提高系统可靠性和简化编程逻辑等好处。这种通信方式在要求高效、稳定和可靠的数据传输场景中特别有用,可以提升系统性能和响应能力。
/* 串口1 GPIO引脚初始化 */
void Uart1GpioInit(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // 使能GPIOA时钟
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; // TX 推挽输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct); // PA9
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU; // RX上拉输入
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA, &GPIO_InitStruct); // PA10
}
/* 串口1配置 */
void Uart1Config(u32 a)
{
USART_InitTypeDef USART_InitStruct; // 串口配置
NVIC_InitTypeDef NVIC_InitStructure; // 中断配置
DMA_InitTypeDef DMA_InitStruct; // DMA 配置
Uart1GpioInit();
USART_DeInit(USART1); // 寄存器恢复默认值
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); // 使能串口时钟
/* 串口参数配置 */
USART_InitStruct.USART_BaudRate = a; // 波特率
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // 无流控
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // 收发
USART_InitStruct.USART_Parity = USART_Parity_Even;//无奇偶校验位 // 偶校验位
USART_InitStruct.USART_StopBits = USART_StopBits_1; // 1个停止位
USART_InitStruct.USART_WordLength = USART_WordLength_8b; // 8个数据位
USART_Init(USART1, &USART_InitStruct);
USART_Cmd(USART1, ENABLE); // 使能串口
/* 串口中断配置 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 子优先级
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; // 串口1中断
NVIC_Init(&NVIC_InitStructure); // 嵌套向量中断控制器初始化
USART_ITConfig(USART1, USART_IT_TC, ENABLE); // 使能串口发送中断,发送完成产生 USART_IT_TC 中断
USART_ITConfig(USART1, USART_IT_IDLE, ENABLE); // 使能串口空闲中断,接收一帧数据产生 USART_IT_IDLE 空闲中断
/* 串口DMA配置 */
DMA_DeInit(DMA1_Channel4); // DMA1 通道4,寄存器复位
DMA_DeInit(DMA1_Channel5); // DMA1 通道5,寄存器复位
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 使能 DMA1 时钟
// RX DMA1 通道5
DMA_InitStruct.DMA_BufferSize = USART_REC_LEN; // 定义了接收的最大长度
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralSRC; // 串口接收,方向是外设->内存
DMA_InitStruct.DMA_M2M = DMA_M2M_Disable; // 本次是外设到内存,所以关闭内存到内存
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)UART1_RX_BUF_TEMP;// 内存的基地址,要存储在哪里
DMA_InitStruct.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;// 内存数据宽度,按照字节存储
DMA_InitStruct.DMA_MemoryInc = DMA_MemoryInc_Enable; // 内存递增,每次串口收到数据存在内存中,下次收到自动存储在内存的下一个位置
DMA_InitStruct.DMA_Mode = DMA_Mode_Normal; // 正常模式
DMA_InitStruct.DMA_PeripheralBaseAddr = USART1_BASE + 0x04; // 外设的基地址,串口的数据寄存器
DMA_InitStruct.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; // 外设的数据宽度,按照字节存储,与内存的数据宽度一致
DMA_InitStruct.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // 接收只有一个数据寄存器 RDR,所以外设地址不递增
DMA_InitStruct.DMA_Priority = DMA_Priority_High; // 优先级
DMA_Init(DMA1_Channel5, &DMA_InitStruct);
// TX DMA1 通道4
DMA_InitStruct.DMA_BufferSize = 0; // 发送缓冲区的大小,初始化为0不发送
DMA_InitStruct.DMA_DIR = DMA_DIR_PeripheralDST; // 发送是方向是内存到外设,外设作为目的地
DMA_InitStruct.DMA_MemoryBaseAddr = (uint32_t)UART1_TX_BUF; // 发送内存地址,从哪里发送
DMA_Init(DMA1_Channel4, &DMA_InitStruct);
USART_DMACmd(USART1, USART_DMAReq_Tx | USART_DMAReq_Rx, ENABLE);// 使能DMA串口发送和接受请求
DMA_Cmd(DMA1_Channel5, ENABLE); // 使能接收
DMA_Cmd(DMA1_Channel4, DISABLE); // 禁止发送
}
/* 清除DMA的传输数量寄存器 */
void uart1DmaClear(void)
{
DMA_Cmd(DMA1_Channel5, DISABLE); // 关闭 DMA1_Channel5 通道
DMA_SetCurrDataCounter(DMA1_Channel5, sizeof(UART1_RX_BUF)); // 重新写入要传输的数据数量
DMA_Cmd(DMA1_Channel5, ENABLE); // 使能 DMA1_Channel5 通道
}
/* 串口1发送数组 */
void uart1SendArray(uint8_t *arr, uint8_t len)
{
if(len == 0) // 判断长度是否有效
{
return;
}
uint8_t sendLen = len > sizeof(UART1_TX_BUF) ? sizeof(UART1_TX_BUF) : len; // 防止越界
while(DMA_GetCurrDataCounter(DMA1_Channel4)); // 检查DMA发送通道内是否还有数据
if(arr)
{
memcpy(UART1_TX_BUF, arr, sendLen);
}
// DMA发送数据-要先关 设置发送长度 开启DMA
DMA_Cmd(DMA1_Channel4, DISABLE);
DMA_SetCurrDataCounter(DMA1_Channel4, sendLen); // 重新写入要传输的数据数量
DMA_Cmd(DMA1_Channel4, ENABLE); // 启动DMA发送
}
void USART1_IRQHandler(void) // 串口1 的中断处理函数
{
uint8_t clear;
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET) // 空闲中断
{
clear = USART1->SR; // 清除空闲中断
clear = USART1->DR; // 清除空闲中断
Receive_Data(); // 接收数据
UART1_RX_Flag = 1; // 置接收标志位
}
if(USART_GetITStatus(USART1, USART_IT_TC) != RESET) // 发送完成
{
USART_ClearITPendingBit(USART1, USART_IT_TC); // 清除完成标记
DMA_Cmd(DMA1_Channel4, DISABLE); // 关闭DMA
UART1_TX_Flag = 1; // 设置发送完成标志位
}
}
3.HLW8032 数据处理
void Data_Processing(void)//HLW8032 数据处理
{
u32 VP_REG = 0, V_REG = 0, CP_REG = 0, C_REG = 0, PP_REG = 0, P_REG = 0;
if(UART1_RX_BUF[0] != 0xaa && UART1_RX_BUF[1] == 0x5a) //芯片误差修正功能正常,参数正常
{
VP_REG = UART1_RX_BUF[2] * 65536 + UART1_RX_BUF[3] * 256 + UART1_RX_BUF[4]; //计算电压参数寄存器
V_REG = UART1_RX_BUF[5] * 65536 + UART1_RX_BUF[6] * 256 + UART1_RX_BUF[7]; //计算电压寄存器
V = ((double)VP_REG / (double)V_REG) * 1.88; //计算电压值,1.88为电压系数,根据所采用的分压电阻大小来确定
CP_REG = UART1_RX_BUF[8] * 65536 + UART1_RX_BUF[9] * 256 + UART1_RX_BUF[10]; //计算电流参数寄存器
C_REG = UART1_RX_BUF[11] * 65536 + UART1_RX_BUF[12] * 256 + UART1_RX_BUF[13]; //计算电流寄存器
C = ((CP_REG * 100) / C_REG) * 10.0; //计算电流值
if(UART1_RX_BUF[0] > 0xf0) //判断实时功率是否未溢出
{
P = 0; //未接用电设备
}
else
{
PP_REG = UART1_RX_BUF[14] * 65536 + UART1_RX_BUF[15] * 256 + UART1_RX_BUF[16]; //计算功率参数寄存
P_REG = UART1_RX_BUF[17] * 65536 + UART1_RX_BUF[18] * 256 + UART1_RX_BUF[19]; //计算功率寄存器
P = (PP_REG / P_REG) * 1.88 * 1; //计算有效功率
}
}
else//芯片误差修正功能失效
{
LCD_Write_String(0, 0, "HLW8032 ERROR");
}
}