471 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			471 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
#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"
 | 
						|
 | 
						|
 | 
						|
 | 
						|
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
 | 
						|
 | 
						|
 | 
						|
 | 
						|
struct _protm_def{
 | 
						|
  uart_def *uart;
 | 
						|
  array_def *buff;
 | 
						|
  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;
 | 
						|
  
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
static void recv_irq(void *t,uint8_t d)
 | 
						|
{
 | 
						|
  protm_def *p=t;
 | 
						|
  arr_append(p->buff,d);
 | 
						|
  switch(arr_length(p->buff)){
 | 
						|
    case 2:
 | 
						|
      if(arr_get(p->buff,0)=='Y'&&arr_get(p->buff,1)=='e')
 | 
						|
      {
 | 
						|
        p->num_to_recv=4;
 | 
						|
      }else{
 | 
						|
        arr_remove(p->buff,0,1);
 | 
						|
        p->num_to_recv=0;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case 4:
 | 
						|
      {
 | 
						|
        int len=arr_get(p->buff,2)|(arr_get(p->buff,3)<<8);
 | 
						|
        p->num_to_recv=len;
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      break;
 | 
						|
  }
 | 
						|
  // 此时一帧数据已完成
 | 
						|
  if(p->num_to_recv>0&&p->num_to_recv==arr_length(p->buff))
 | 
						|
  {
 | 
						|
    rt_event_send(p->event,EVENT_RECV);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
/*
 | 
						|
* 与从机通信的串口是串行的,一个通信事务完成之后才能开始下一个通信
 | 
						|
* 协议层接收的通信事务是并行的,应用层来的通信任务会被放入队列
 | 
						|
*/
 | 
						|
 | 
						|
static void protm_send_next(void *t);
 | 
						|
int protm_send(protm_def *p,send_data_def *d);
 | 
						|
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)
 | 
						|
    {
 | 
						|
      // DBG_LOG("protmcu:recv event.");
 | 
						|
      data=arr_duplicate(p->buff);
 | 
						|
      arr_clear(p->buff);
 | 
						|
      //DBG_LOG("recv:%s",str_temp(arr_string(data)));
 | 
						|
      decode_data=protm_decode(p,data);
 | 
						|
      arr_delete(data);
 | 
						|
      emit protm_recv_signal(p,p->recv_src,p->recv_cmd,arr_temp(decode_data),str_temp(str_duplicate(p->str_err)));
 | 
						|
      irq_disable();
 | 
						|
      p->num_to_recv=0;
 | 
						|
      irq_enable();
 | 
						|
      protm_send_next(p);
 | 
						|
    }
 | 
						|
    else if(ev&EVENT_TIMEOUT)
 | 
						|
    {
 | 
						|
      protm_send_timeout(p);
 | 
						|
    }
 | 
						|
    if (ev&EVENT_SEND)
 | 
						|
    {
 | 
						|
      // DBG_LOG("protmcu:send event.");
 | 
						|
      if(list_length(p->send_data)>0)
 | 
						|
      {
 | 
						|
        send_data_def *a=(send_data_def *)list_get(p->send_data,0);
 | 
						|
        protm_send(p,a);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    ev=0;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// 发送下一个
 | 
						|
static void protm_send_next(void *t)
 | 
						|
{
 | 
						|
  protm_def *p=t;
 | 
						|
  rt_timer_stop(p->timer);
 | 
						|
  list_remove(p->send_data,0);
 | 
						|
  // 如果发送列表中有数据,此时发送
 | 
						|
  if (list_length(p->send_data)>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;
 | 
						|
  send_data_def *s=list_get(p->send_data,0);
 | 
						|
  protm_slave *sl=protm_get_slave(p,s->addr);
 | 
						|
  if(sl->retry>0){
 | 
						|
    sl->retry--;
 | 
						|
    DBG_WARN("slave:%d retry left ",s->addr,sl->retry);
 | 
						|
    list_shift(p->send_data);
 | 
						|
  }else{
 | 
						|
    DBG_WARN("slave:%d retry timeout,remove the send data.",s->addr);
 | 
						|
    list_remove(p->send_data,0);
 | 
						|
  }
 | 
						|
  // 如果发送列表中有数据,此时发送
 | 
						|
  if (list_length(p->send_data)>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 int 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=arr_creat();
 | 
						|
  p->run=1;
 | 
						|
  p->slaves=list_creat(sizeof(protm_slave),protm_slave_sub,protm_slave_del,0);
 | 
						|
  p->slaves_addr=list_creat_int();
 | 
						|
  p->send_data=list_creat(sizeof(send_data_def),0,_list_send_data_del,0);
 | 
						|
  // 创建超时定时器,超过指定时间则不再等待从机返回
 | 
						|
  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,512,5,20);
 | 
						|
  rt_thread_startup(rt_t);
 | 
						|
  p->uart->init(p->uart);
 | 
						|
  p->uart->set_irq(p->uart,recv_irq,p);
 | 
						|
  
 | 
						|
  count++;
 | 
						|
  return p;
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
// 得到指定地址的从机,找不到则添加
 | 
						|
protm_slave *protm_get_slave(protm_def *p,uint8_t addr)
 | 
						|
{
 | 
						|
  protm_slave *r=0;
 | 
						|
  for(int i=0;i<list_length(p->slaves);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;i<list_length(p->slaves);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.");
 | 
						|
    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,send_data_def *d)
 | 
						|
{
 | 
						|
  rt_tick_t tick=0;
 | 
						|
  protm_slave *s=protm_get_slave(p,d->addr);
 | 
						|
  //DBG_LOG("send:%s",str_temp(arr_string(d->data)));
 | 
						|
  tick=rt_tick_from_millisecond(s->timeout_ms);
 | 
						|
  rt_timer_control(p->timer,RT_TIMER_CTRL_SET_TIME,&tick);
 | 
						|
  rt_timer_start(p->timer);
 | 
						|
  if(d->data==0)
 | 
						|
  {
 | 
						|
    DBG_WARN("addr=%d,d->data=0",d->addr);
 | 
						|
    return 0;
 | 
						|
  }
 | 
						|
  return p->uart->write(p->uart,arr_data(d->data),arr_length(d->data));
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
//// 槽函数,发送数据到指定地址
 | 
						|
//void protm_send_call(protm_def *p,list_def *addrs/*int*/,uint8_t cmd,list_def *comp_cmd/*int*/,array_def *data)
 | 
						|
//{
 | 
						|
//  param_check(p);
 | 
						|
//  for(int i=0;i<list_length(addrs);i++)
 | 
						|
//  {
 | 
						|
//    uint8_t dst=(uint8_t)list_get_int(addrs,i);
 | 
						|
//    if(list_contains(p->slaves_addr,(int []){dst})==1)
 | 
						|
//    {
 | 
						|
//      array_def *t=0;
 | 
						|
//      t=protm_encode(p,dst,cmd,comp_cmd,data);
 | 
						|
//      protm_send(p,t);
 | 
						|
//      arr_delete(t);
 | 
						|
//    }
 | 
						|
//  }
 | 
						|
//}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
// 槽函数,发送数据到指定地址
 | 
						|
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;
 | 
						|
    list_append(p->send_data,&sd);
 | 
						|
    if(p->in_send==0){
 | 
						|
      irq_disable();
 | 
						|
      p->in_send=1;
 | 
						|
      irq_enable();
 | 
						|
      //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});
 | 
						|
}
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 | 
						|
 |