#include "stdio.h" #include "rtthread.h" #include "board.h" #include "mystdlib.h" #include "list.h" #include "mystring.h" #include "signal.h" #include "prot_mcu.h" #include "buff.h" #include "bytearray.h" #include "debug.h" #include "crc.h" #include "mymisc.h" //#define DBG_WARN(...) //#define DBG_LOG(...) typedef struct{ uint16_t no; list_def *cmds;//int uint8_t addr; uint8_t cmd; array_def *data; int retry; int timeout_ms; }protm_slave; static int protm_slave_sub(void *a,void *b) { protm_slave *a_=a; protm_slave *b_=b; return a_->addr-b_->addr; } static int protm_slave_del(void *t) { protm_slave *p=t; CHECK_DO(p->cmds,list_delete); return 0; } static protm_slave *protm_slave_creat(void) { protm_slave *p=calloc(1,sizeof(protm_slave)); param_check(p); return p; } array_def *protm_decode(protm_def *p,array_def *data); protm_slave *protm_get_slave(protm_def *p,uint8_t addr); typedef struct{ array_def *data; int addr; }send_data_def; // 定义事件 #define EVENT_RECV 0x1 #define EVENT_SEND 0x2 #define EVENT_TIMEOUT 0x4 // 接收buff大小 #define RECV_BUFF_SIZE 300 struct _protm_def{ uart_def *uart; uint8_t *buff; int buff_index; int num_to_recv; rt_event_t event; rt_timer_t timer; int run; char *str_err; list_def *slaves;//protm_slave list_def *slaves_addr;//int //list_def *send_data;//send_data_def int in_send;//在发送状态时为1 uint8_t recv_cmd; uint8_t recv_src; int send_index;//发送时指向对应的从机 }; static void recv_irq(void *t,uint8_t d) { protm_def *p=t; p->buff[p->buff_index]=d; p->buff_index++; switch(p->buff_index){ case 2: if(p->buff[0]=='Y'&&p->buff[1]=='e') { p->num_to_recv=4; }else{ p->buff[0]=p->buff[1]; p->buff_index--; p->num_to_recv=0; } break; case 4: { int len=p->buff[2]|((p->buff[3])<<8); p->num_to_recv=len; } break; default: break; } // 此时一帧数据已完成 if(p->num_to_recv>0&&p->num_to_recv==p->buff_index) { rt_event_send(p->event,EVENT_RECV); } } static void do_later(void *t) { protm_def *p=t; DBG_LOG("recv end irq rx_buff=%08x.",p->buff); rt_event_send(p->event,EVENT_RECV); } static void recv_end_irq(void *t,uint32_t len) { protm_def *p=t; p->num_to_recv=len; p->buff_index=len; rt_event_send(p->event,EVENT_RECV); //later_execute(do_later,t,1); } /* * 与从机通信的串口是串行的,一个通信事务完成之后才能开始下一个通信 * 协议层接收的通信事务是并行的,应用层来的通信任务会被放入队列 */ static void protm_send_next(void *t); int protm_send(protm_def *p,protm_slave *s); static void protm_send_timeout(void *t); static void protm_run(void *t) { protm_def *p=t; array_def *data; array_def *decode_data; uint32_t ev; while(p->run) { rt_event_recv(p->event,0xffffffff,RT_EVENT_FLAG_OR|RT_EVENT_FLAG_CLEAR,RT_WAITING_FOREVER,&ev); if(ev&EVENT_RECV) { data=arr_creat(); arr_appends(data,p->buff,p->buff_index); //DBG_LOG("recv:%s",str_temp(arr_string(data))); p->num_to_recv=0; p->buff_index=0; decode_data=protm_decode(p,data); if(strcmp(p->str_err,"ok")==0){ //DBG_LOG("slave:%d,",p->recv_src); protm_send_next(p); emit protm_recv_signal(p,p->recv_src,p->recv_cmd,arr_temp(decode_data),str_temp(str_duplicate(p->str_err))); }else{ DBG_WARN("slave:%d, %s",p->recv_src,str_temp(arr_string(data))); } arr_delete(data); } else if(ev&EVENT_TIMEOUT) { protm_send_timeout(p); } if (ev&EVENT_SEND) { //DBG_LOG("protmcu:send event."); if(p->send_index>=0) { protm_slave *s=(protm_slave *)list_get(p->slaves,p->send_index); //DBG_LOG("send to:%d",s->addr); protm_send(p,s); } } ev=0; } } static int protm_find_next(protm_def *p) { for(int i=0;islaves);i++) { p->send_index++; if(p->send_index>=list_length(p->slaves)) p->send_index=0; protm_slave *s=list_get(p->slaves,p->send_index); if(s->data){ return p->send_index; } } p->send_index=-1; return p->send_index; } // 发送下一个 static void protm_send_next(void *t) { protm_def *p=t; //DBG_LOG("timer stop"); rt_timer_stop(p->timer); protm_slave *s=protm_get_slave(p,p->recv_src); //DBG_LOG("delete data"); arr_delete(s->data); s->retry=0; // 如果发送列表中有数据,此时发送 if (protm_find_next(p)>=0) { // DBG_LOG("protmcu:EVENT_SEND."); rt_event_send(p->event,EVENT_SEND); } else{ irq_disable(); p->in_send=0; irq_enable(); } } static void protm_send_timeout_cb(void *t) { protm_def *p=t; rt_event_send(p->event,EVENT_TIMEOUT); } // 接收超时 static void protm_send_timeout(void *t) { protm_def *p=t; protm_slave *s=list_get(p->slaves,p->send_index); if(s->retry>0){ s->retry--; //DBG_WARN("slave:%d retry left %d",s->addr,s->retry); }else{ DBG_WARN("slave:%d retry timeout,remove the send data.",s->addr); arr_delete(s->data); } // 如果发送列表中有数据,此时发送 if (protm_find_next(p)>=0) { // DBG_LOG("protmcu:EVENT_SEND."); rt_event_send(p->event,EVENT_SEND); } else{ irq_disable(); p->in_send=0; irq_enable(); } } // 发送队列删除条目 static int _list_send_data_del(void *t) { send_data_def *a=t; arr_delete(a->data); return 0; } protm_def *protm_creat(uart_def *uart,int *addrs,int num) { static uint16_t count=0; char name[20]={0}; protm_def *p=calloc(1,sizeof(protm_def)); param_check(p); p->uart=uart; sprintf(name,"protm_event#%d",count); p->event=rt_event_create(name,RT_IPC_FLAG_FIFO); p->buff=malloc(RECV_BUFF_SIZE); p->run=1; p->slaves=list_creat(sizeof(protm_slave),protm_slave_sub,protm_slave_del,0); p->slaves_addr=list_creat_int(); // 创建超时定时器,超过指定时间则不再等待从机返回 p->timer=rt_timer_create("port_timer",protm_send_timeout_cb,p, rt_tick_from_millisecond(55), RT_TIMER_FLAG_ONE_SHOT|RT_TIMER_FLAG_SOFT_TIMER); list_appends(p->slaves_addr,addrs,num); sprintf(name,"protm_t#%d",count); rt_thread_t rt_t=rt_thread_create(name,protm_run,p,2048,5+count,20); rt_thread_startup(rt_t); p->uart->init(p->uart,0); //p->uart->set_irq(p->uart,recv_irq,p); p->uart->set_end_irq(p->uart,p->buff,RECV_BUFF_SIZE,recv_end_irq,p); count++; return p; } // 得到指定地址的从机,找不到则添加 protm_slave *protm_get_slave(protm_def *p,uint8_t addr) { protm_slave *r=0; for(int i=0;islaves);i++) { r=list_get(p->slaves,i); if(r->addr==addr) return r; } r=protm_slave_creat(); r->addr=addr; r->cmds=list_creat_int(); r->no=0; list_append(p->slaves,r); free(r); return list_get(p->slaves,-1); } // 设置所有从机流水号 void protm_set_no_all(protm_def *p,uint16_t no,list_def *cmd/*int*/) { for(int i=0;islaves);i++) { protm_slave *s=list_get(p->slaves,i); s->no=no; list_clear(s->cmds); list_append_from(s->cmds,cmd); } } // 设置单个从机流水号 void protm_set_no(protm_def *p,uint8_t addr,uint16_t no,list_def *cmd/*int*/) { protm_slave *s=protm_get_slave(p,addr); s->no=no; list_clear(s->cmds); list_append_from(s->cmds,cmd); } // 解码 array_def *protm_decode(protm_def *p,array_def *data) { array_def *r=arr_creat(); param_check(r); str_set(p->str_err,"ok"); if(arr_length(data)<10) { DBG_WARN("recv data len too less."); DBG_WARN("data=%s",str_temp(arr_string(data))); str_set(p->str_err,"recv data len too less."); return r; } uint8_t src=arr_get(data,4); p->recv_src=src; uint16_t len=arr_get(data,2)|(arr_get(data,3)<<8); uint8_t crc=crc_crc8(arr_data(data),arr_length(data)-1); if(len!=arr_length(data)) { // 如果长度不相等则产生了数据丢失 DBG_WARN("recv data have lossed."); str_set(p->str_err,"recv data have lossed."); return r; } uint16_t no=arr_get(data,7)|(arr_get(data,8)<<8); uint16_t h_no=protm_get_slave(p,src)->no; if(no!=h_no) { // 发送一条指令等待其返回,此时流水号应相同 //DBG_WARN("slave_addr=%d cmd_no error:h_no=%d,no=%d.",src,h_no,no); //str_set(p->str_err,"cmd no err."); //return r; } if(crc!=arr_get(data,-1)) { DBG_WARN("recv data crc check error:%02x,%02x.",crc,arr_get(data,-1)); str_set(p->str_err,"crc check err."); } p->recv_cmd=arr_get(data,6); list_def *cmds=protm_get_slave(p,src)->cmds; if(list_contains(cmds,(int []){p->recv_cmd})==0){ // 命令号校验不对 DBG_WARN("cmd check err.cmds=%s,recv_cmd=%d",tappend(list_string(cmds),0),p->recv_cmd); str_set(p->str_err,"cmd check err."); } arr_delete(r); return arr_mid(data,9,len-10); } // 编码 array_def *protm_encode(protm_def *p,uint8_t dst,uint8_t cmd,list_def *comp_cmd/*int*/,array_def *data) { array_def *t=arr_creat(); param_check(t); uint16_t len=arr_length(data)+10; protm_slave *slave=protm_get_slave(p,dst); slave->no++; protm_set_no(p,dst,slave->no,comp_cmd); arr_append(t,'Y'); arr_append(t,'e'); arr_append(t,len&0xff); arr_append(t,len>>8); arr_append(t,0);// 源地址 arr_append(t,dst);// 目标地址 arr_append(t,cmd);// 命令码 arr_append(t,slave->no&0xff); arr_append(t,slave->no>>8); arr_appends_from(t,data); arr_append(t,crc_crc8(arr_data(t),arr_length(t))); return t; } // 发送数据,发送前先开启超时定时器 int protm_send(protm_def *p,protm_slave *s) { rt_tick_t tick=0; //DBG_LOG("send:%s",str_temp(arr_string(s->data))); tick=rt_tick_from_millisecond(s->timeout_ms); rt_timer_control(p->timer,RT_TIMER_CTRL_SET_TIME,&tick); //DBG_LOG("timer start"); rt_timer_start(p->timer); if(s->data==0) { DBG_WARN("addr=%d,d->data=0",s->addr); return 0; } return p->uart->write(p->uart,arr_data(s->data),arr_length(s->data)); } // 槽函数,发送数据到指定地址 void protm_send_call(protm_def *p,uint8_t addr,uint8_t cmd,list_def *comp_cmd/*int*/,array_def *data,int timeout_ms,int retry) { param_check(p); if(list_contains(p->slaves_addr,(int []){addr})==1) { array_def *t=0; t=protm_encode(p,addr,cmd,comp_cmd,data); //protm_send(p,t); //arr_delete(t); // 添加到发送队列,发送必须等到回应或超时才能发送下一个 //DBG_LOG("send to:%d",addr); send_data_def sd={0}; sd.addr=addr;sd.data=t; protm_slave *s=protm_get_slave(p,addr); s->retry=retry; s->timeout_ms=timeout_ms; if(s->data){ DBG_WARN("s->data not null."); arr_delete(s->data); } s->data=t; if(p->in_send==0){ irq_disable(); p->in_send=1; irq_enable(); protm_find_next(p); //DBG_LOG("send call"); rt_event_send(p->event,EVENT_SEND); } } } // 返回1表示此地址可以发送到这个端口 int protm_contains(protm_def *p,uint8_t addr) { param_check(p); return list_contains(p->slaves_addr,(int []){addr}); }