串口屏作为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通讯,只需关注数据是否被修改,如要显示显示,将数据表相应单元的填充数据即可