【沁恒RISC-V内核 CH582】恒温控制取暖器
[复制链接]
前一陈子,用手机APP控制取暖器:【沁恒RISC-V内核 CH582】PWM 控制取暖器 - 国产芯片交流 - 电子工程世界-论坛 (eeworld.com.cn),结合我参与迪文屏的活动,也制作了一个恒温的取暖器,这里展示一下给大家:
【实现控制原理】迪文屏由UART0与CH582进行通信,发送开、关,或者预期温度值。CH582运行TMOS进行串口接收与AHT10的温度值进行采样,组成温度控制系统。
【迪文屏实现步聚】:
1、制作背景图片:
2、设置按键变量等如下图:
具体实现在这里不是重点,我附具体工程在后面。
【CH582】实现方式:
1、UART0与迪文屏进行通信,首先初始化迪文屏,中断接收。同时初始化定时器1,实现1ms产生一个中断,进行计时:
/*
* 初始串口0、1
*/
void SerialInit(void)
{
/* 配置串口1:先配置IO口模式,再配置串口 */
GPIOA_SetBits( GPIO_Pin_9 );
GPIOA_ModeCfg( GPIO_Pin_8, GPIO_ModeIN_PU ); // RXD-配置上拉输入
GPIOA_ModeCfg( GPIO_Pin_9, GPIO_ModeOut_PP_5mA ); // TXD-配置推挽输出,注意先让IO口输出高电平
UART1_DefInit();
//继电器加热工作输出
GPIOB_SetBits(GPIO_Pin_19);
GPIOB_ModeCfg(GPIO_Pin_19, GPIO_ModeOut_PP_20mA);
/* 配置串口0:先配置IO口模式,再配置串口 */
GPIOB_SetBits( GPIO_Pin_7);
GPIOB_ModeCfg( GPIO_Pin_4, GPIO_ModeIN_PU ); // RXD-配置上拉输入
GPIOB_ModeCfg( GPIO_Pin_7, GPIO_ModeOut_PP_5mA ); // TXD-配置推挽输出,注意先让IO口输出高电平
UART0_DefInit();
UART0_ByteTrigCfg( UART_1BYTE_TRIG ); //这里设置接收到1字节就产生中断,实现不定长接收
UART0_INTCfg( ENABLE, RB_IER_RECV_RDY|RB_IER_LINE_STAT );
PFIC_EnableIRQ( UART0_IRQn );
TMR0_TimerInit(FREQ_SYS / 1000); // 设置定时时间 1ms
TMR0_ITCfg(ENABLE, TMR0_3_IT_CYC_END); // 开启中断
PFIC_EnableIRQ(TMR0_IRQn);
}
2、定义UART0中断函数:
__INTERRUPT
__HIGH_CODE
void UART0_IRQHandler( void )
{
UINT8 ch;
switch ( UART0_GetITFlag() )
{
case UART_II_LINE_STAT : // 线路状态错误
{
UART0_GetLinSTA();
break;
}
case UART_II_RECV_RDY : // 数据达到设置触发点
ch=UART0_RecvByte();
Serial0Recv(ch);
break;
case UART_II_RECV_TOUT : // 接收超时,暂时一帧数据接收完成
break;
case UART_II_THR_EMPTY : // 发送缓存区空,可继续发送
break;
case UART_II_MODEM_CHG : // 只支持串口0
break;
default :
break;
}
}
3、定时器1函数
__INTERRUPT
__HIGH_CODE
void TMR0_IRQHandler( void ) // TMR0 定时中断
{
if ( TMR0_GetITFlag( TMR0_3_IT_CYC_END ) )
{
TMR0_ClearITFlag( TMR0_3_IT_CYC_END ); // 清除中断标志
SerialRecvTimeout(); //1ms时间到了去检测是否超时
}
}
4、迪文屏串口接收、处理:
void task_uart0_rec(void)
{
int i , l;
if (SerialStr0.RecvLen > 0 ) {
PRINT("RECV_LEN:%d",SerialStr0.RecvLen);
if (SerialStr0.RecvLen == (SerialStr0.RecvBuff[2]+3) \
&& SerialStr0.RecvBuff[0] == 0x5A \
&& SerialStr0.RecvBuff[1] == 0xA5)
{
//接收到正确的数据
DMG85480.state = RECV_WAIT_EXPLAN;
DMG85480.data_len = SerialStr0.RecvBuff[2]-3;
DMG85480.commad = SerialStr0.RecvBuff[3];
PRINT("recv_CMMD:%X\n",DMG85480.commad);
DMG85480.address = (uint16_t)SerialStr0.RecvBuff[4]<<8 | SerialStr0.RecvBuff[5];
PRINT("recv_ADDRS:%X\n",DMG85480.address);
i = SerialStr0.RecvBuff[2]-3;
for (l = 0; l < i; ++l) {
DMG85480.data[l] = SerialStr0.RecvBuff[l+6];
}
rev_explain();
DMG85480.DMG_ERROR = RECV_OK;
}
else {
DMG85480.DMG_ERROR = RECV_ERR;
SerialStr0.RecvLen = 0;//丢弃无效的包
}
}
SerialStr0.RecvLen = 0;
}
5、迪文屏命令处理:
/*
* 数据判断
*
*/
static void rev_explain(void)
{
uint8_t send_data[10];
switch (DMG85480.commad) {
case WRIT_DMG_DATA:
if (DMG85480.address == SWITCH) {//如果是开关按键
if (DMG85480.data[2] == 0x01) {//按下的是开键,转换到页面。
PRINT("OPEN\n");
Electric_Heater.work_state = OPEN;
send_data[0] = 0x5A;
send_data[1] = 0x01;
send_data[2] = 0x00;
send_data[3] = 0x01;
DMG85480.state = IDEL;
send_DMG(send_data, CHANG_PAGE, WRIT_DMG_CMD,4);
//等待收到OK
//设置开机模式
}
else if (DMG85480.data[2] == 0x00) { //关断了
//关闭电加热丝 切换到0号页面
Electric_Heater.work_state = CLOSE;
PRINT("CLOSE\n");
send_data[0] = 0x5A;
send_data[1] = 0x01;
send_data[2] = 0x00;
send_data[3] = 0x00;
DMG85480.state = IDEL;
send_DMG(send_data, CHANG_PAGE, WRIT_DMG_CMD,4);
}
}
else if (DMG85480.address == SET_TEMP) { //温度设定改变
//设置温度值
PRINT("CHANGE\n");
Electric_Heater.set_temp = DMG85480.data[2];
DMG85480.state = IDEL;
}
break;
case WRIT_DMG_CMD:
if (DMG85480.address == SEND_RECV_OK) {//返回写正确
DMG85480.DMG_ERROR = SEND_OK;
DMG85480.state = IDEL;
}
break;
default:
break;
}
}
7、迪文屏发送函数封装:
/*
* 发送给串口屏
* 参数1:数据
* 参数2:指令 0x80-0x83
* 参数3:串口屏地址
* 参数4:数据长度
*/
void send_DMG(uint8_t pdata[], uint16_t addr, uint8_t commd, uint8_t data_len)
{
uint8_t commd_len;
while(DMG85480.state != IDEL);
SerialStr0.TxBuff[0] = 0x5a;
SerialStr0.TxBuff[1] = 0xa5;
SerialStr0.TxBuff[2] = data_len + 3;//长度
SerialStr0.TxBuff[3] = commd;
SerialStr0.TxBuff[4] = (uint8_t)(addr>>8);
SerialStr0.TxBuff[5] = (uint8_t)addr;
for (commd_len = 0; commd_len < data_len; ++commd_len) {
SerialStr0.TxBuff[6+commd_len] = pdata[commd_len];
}
DMG85480.state = SEND_WAIT;
UART0_SendString(SerialStr0.TxBuff, 6+data_len);
DMG85480.state = IDEL;
}
【AHT10温度采集】AHT10每1秒采集一次环境温度,更新到取暖器的当前温度值,与设置温度进行对比,来确定继电器的开关。
//加热器状况
typedef struct{
uint8_t work_state; //是否打开
uint8_t set_temp; //期望温度
uint8_t this_temp; //目前温度值
uint8_t old_temp;
uint32_t work_time; //工作时间
uint32_t stop_work_time; //休息时间
uint32_t next_work_time; //等待时间
} _Electric_Heater;
【取暖器控制】1、先判断work_state,如果来确定是否打开关闭继电器。
2、如果开的状态,则比较当前温度与预设温度,来确定开关的状态。
_Electric_Heater Electric_Heater;
void select_working_mode(void)
{
if (Electric_Heater.this_temp < Electric_Heater.set_temp && Electric_Heater.work_state == OPEN)
{
Electric_Heater.work_state = OPEN;
} else if (Electric_Heater.this_temp > Electric_Heater.set_temp && Electric_Heater.work_state == OPEN) {
Electric_Heater.work_state = WAITING;
Electric_Heater.stop_work_time = 5;
}
else if (Electric_Heater.work_state == WAITING && Electric_Heater.stop_work_time>0 ) {
Electric_Heater.work_state = WAITING;
Electric_Heater.stop_work_time --;
// mDelaymS(1000);
}
else if (Electric_Heater.work_state == WAITING && Electric_Heater.stop_work_time == 0 )
{
Electric_Heater.work_state = OPEN;
}
}
3、继电器工作任务函数:
void select_hearter_work(void)
{
//继电器加热工作输出
static uint8_t old_temp;
//void send_DMG(uint8_t pdata[], uint16_t addr, uint8_t commd, uint8_t data_len)
uint8_t temp[10] = {0};
if (Electric_Heater.work_state != CLOSE) {
if (Electric_Heater.this_temp != Electric_Heater.old_temp) {
Electric_Heater.old_temp = Electric_Heater.this_temp;
temp[1] = Electric_Heater.this_temp;
send_DMG(temp,DISP_TEMP,WRIT_DMG_CMD,2);
}
temp[0] = 0x5a;
temp[1] = 0xa5;
temp[2] = 0x01;
temp[3] = 0x00;//0通道
temp[4] = 0x00;
temp[5] = 0x01;//1个数据
temp[6] = 0x00;
temp[7] = Electric_Heater.this_temp;
send_DMG(temp,LINE_ADDR,WRIT_DMG_CMD,8);
}
select_working_mode();
if (Electric_Heater.work_state == OPEN) {
//开启加热
PRINT("STATE == OPEN\n");
GPIOB_SetBits(GPIO_Pin_19);
}
else {
PRINT("STATE == CLOSE\n");
GPIOB_ResetBits(GPIO_Pin_19);
}
select_working_mode();
}
【TMOS任务创建】:1、创建任务ID、事件函数
static uint8_t Peripheral_TaskID = INVALID_TASK_ID; // Task ID for internal task/event processing
static uint8_t AH10_TaskID = AH10_EVT;
static uint8_t DMG_TaskID = DMG_EVT;
static uint8_t HEATER_TaskID = HEATER_EVT;
uint16_t AH10_ProcessEvent(uint8_t task_id, uint16_t events);
uint16_t DMG_ProcessEvent(uint8_t task_id, uint16_t events);
uint16_t HEATER_ProcessEvent(uint8_t task_id, uint16_t events);
2、入口函数:
uint16_t AH10_ProcessEvent(uint8_t task_id, uint16_t events)
{
if ( events & AH10_EVT ) {
AHT10ReadData(temperate,humidity);
return (events ^ AH10_EVT);
}
return 0;
}
uint16_t DMG_ProcessEvent(uint8_t task_id, uint16_t events)
{
if ( events & DMG_EVT ) {
dmg_work();
return (events ^ DMG_EVT);
}
return 0;
}
uint16_t HEATER_ProcessEvent(uint8_t task_id, uint16_t events)
{
if ( events & HEATER_EVT ) {
select_hearter_work();
return (events ^ HEATER_EVT);
}
return 0;
}
3、主任务添加循环调用的命令:
tmos_start_reload_task(DMG_TaskID, DMG_EVT, 500);//迪文屏通讯任务
tmos_start_reload_task(AH10_TaskID,AH10_EVT,1600);//温度采集
tmos_start_reload_task(HEATER_TaskID, HEATER_EVT, 6000);//继电器控制
【实现效果】:
|