串口屏作为modbus主机,下位机主板作为从机,在从机中建立一张数据表与串口屏作为数据交换缓冲,从机移植freemodbus协议栈,定时响应串口屏的轮询
如下,在一个项目中建立的数据表
//上报给屏的槽状态
typedef struct _db_slot_status{uint16_t header_cnt_before_renewing; //换油前烹饪头数uint16_t header_cnt_total; //总计头数uint16_t cooking_cnt_total; //烹饪计数db_time_t recvy_time; //槽回温时间db_time_t cooking_time; //烹饪时间uint16_t temp; //槽温度uint16_t target_temp; //设定目标温度uint16_t cooking_mode; //关机模式,融油模式,节能模式,预热模式,烹饪模式,滤油模式uint16_t cooking_seg; //当前段号uint16_t cooking_status; //烹饪状态,1工作或0停止db_time_t total_time; //烹饪的总时间uint16_t preheat_highlight; //预热位,一个位对应一个菜单,置1高亮
}db_slot_status_t;//主板上报给屏的状态
typedef union _db_status{uint16_t zone[DB_STATUS_SIZE];struct{uint16_t second; //主板时间秒uint16_t minute; //主板时间分uint16_t hour; //主板时间时uint16_t day; //主板时间日uint16_t month; //主板时间月uint16_t year; //主板时间年uint16_t week; //主板时间星期
// uint16_t firmware_ver; //固件版本
// uint16_t hardware_ver; //对应的硬件版本
// uint16_t serial_number[4]; //序列号
// uint16_t compile_date[12]; //编译日期uint16_t cooking_cnt[DB_MENU_MAX]; //产品烹饪计数uint16_t wifi_status; //0-连接断开,1-连接WFI,2-连接服务器db_slot_status_t slot_l_status; //左槽状态db_slot_status_t slot_r_status; //右槽状态}data;
}db_status_t;//自检数据
typedef union _db_selftest{uint16_t zone[DB_SELFTEST_SIZE];struct{uint16_t input1;uint16_t input2;uint16_t input3;uint16_t input4;uint16_t input5;uint16_t input6;uint16_t left_temp;uint16_t right_temp;uint16_t cpu_temp;uint16_t voltage;uint16_t current;}data;
}db_selftest_t;typedef struct _db_para{//这两组参数是用户操作的即时控制及上报,不需保存db_ctrl_t ctrl; //控制参数db_status_t status; //状态参数
// db_selftest_t selftest; //自检状态//这两组参数需要保存到主板db_function_t function; //功能参数db_cooking_menu_t cooking_menu[DB_MENU_MAX]; //菜单参数}db_para_t;
modbus初始化将modbus指针指向建立的数据表
db_para_ptr = (db_para_t*)usSRegHoldBuf;
以下为modbus任务线程,检查数据是否有被用户修改,如被修改进行用户操作处理
void mlcd_check_variation(void)
{static uint8_t crc=0,crc_new,user_update=0;static time_t last_time;if( crc==0 ){//计算除去状态后的参数校验码crc = crc8_cal((uint8_t*)db_para_ptr+DB_START_ADDR, DB_SAVE_LEN);}//数据被用户通过屏幕修改crc_new = crc8_cal((uint8_t*)db_para_ptr+DB_START_ADDR, DB_SAVE_LEN);if( crc != crc_new ){crc = crc_new;last_time = time(RT_NULL);user_update = 1;}//用户更新10秒后保存数据else if( user_update ){if( time(RT_NULL)-last_time > 10 ){save_flag = 1;user_update = 0;}}
}static void mlcd_task(void *parameter)
{eMBInit( MB_RTU, 0x01, 2, 115200, MB_PAR_NONE );eMBEnable();rt_tick_t tick = rt_tick_get();while(1){eMBPoll();mlcd_check_variation();tick_invent = rt_tick_get()-tick;if( tick_invent>200 )tick_cnt++;tick = rt_tick_get();rt_thread_delay(10);}
}
由于串口屏是以地址方式定位数据单元,以下两个宏用于获取数据表中相应单元的地址比较简便的方法
/** 计算结构体成员偏移地址 */
#define offsetof(TYPE, MEMBER) ((int)(&((TYPE *)0)->MEMBER))/** 根据成员地址获取结构首指针 */
#define container_of(ptr, type, member) ({ \const typeof( ((type *)0)->member ) *__mptr = (ptr); \(type *)( (char *)__mptr - offsetof(type,member) );})
这种方式驱动串口屏不用关心modbus通讯,只需关注数据是否被修改,如要显示显示,将数据表相应单元的填充数据即可