UART ISR Tx Rx架构

Jon*_*han 6 c architecture serial-port serial-communication uart

我让事情复杂化了吗?

我正在构建我的代码,通过UART从8051微型到外围设备进行通信.外设响应来自主机的命令,并且一次只能响应一个命令.这是一个简单的发送和接收协议.(tx1,rx1,tx2,rx2,tx3,rx3)每个TX消息以CR终止,每个响应以>终止.在收到对最后一个的回复之前,我无法发送新消息.如果启用该选项,响应也可以在开头回显打印原始TX消息(但这会导致更多流量)

一个示例消息是:

  • 德克萨斯:你好
  • RX:世界!>

或者使用echo选项......

  • 德克萨斯:你好
  • RX:你好\ rWorld!>

选项A getHello等函数由发送和接收组成.并行ISR例程将收集传入的字节,并在收到">"字符时抛出一个标志.

char* getHello(char * buf){
    sendMsg("Hello\r");
    delay(10ms); //wait a little bit

    //wait for receive to come in or timeout to occur
    while(!receiveFlag || !timeoutFlag);  //thrown by ISR
    receiveMsg(buf);
    //parse the message and do some other stuff
    return buf;
}
Run Code Online (Sandbox Code Playgroud)

优点:

  • 一切都包含在一个功能中.
  • 更容易调试

缺点:

  • 此功能正在阻塞,如果外设从不响应,则可能会挂起,因此必须执行超时.
  • 消息不能无序接收,必须串联(即tx1,rx1,tx2,rx2,tx3,rx3)

选项B 采用并行方法.将创建两个单独的功能.一个用于发送消息,另一个用于在收到ISR的响应时顶点.

void sendHello(){
    sendMsg("Hello\r");
    //do some other stuff if needed
}

char* receiveMsg(char * buf){
    //figure out from echo print what the tx message was
    //use a switch statement to decide which response parser to call
    switch(txMessage){ //pseudo code
    case "Hello":
        receiveMsg(buf);
        //parse the message and do some other stuff
        break;
    }
    return buf;
}
Run Code Online (Sandbox Code Playgroud)

优点:

  • 可以处理无序返回的并行消息,因为它依赖于tx消息的回显打印来弄清楚如何解析它.(即tx1,tx2,tx3,rx1,rx2,rx3)

缺点:

  • 很难调试
  • 产生多个线程
  • 很多额外的代码
  • 不值得,因为消息肯定会按顺序返回

现在,我正在做选项B,但随着我继续该项目,我开始觉得这变得过于复杂.我很好奇你们的想法.

谢谢!

Kei*_*las 4

不过,我倾向于做这种事情,我倾向于有一个单独的串行端口“类”(结构+函数)和一个位于串行端口之上的协议类。我一直在我的嵌入式系统中使用这些。这为您提供了两全其美的功能,即阻塞同步调用和异步调用,以便您可以伪多任务。

typedef struct serial_port_s serial_port;
typedef void (*serial_on_recived_proc)(serial_port* p);
typedef struct serial_port_s{
    bool timeoutFlag;
    bool receiveFlag;
    void* context;
    serial_on_recived_proc response_handler;
};

void send_serial(serial_port* p, char* message)
{
    //SendMsg?
}
void receive_serial(serial_port* p, char* response)
{
    //receiveMsg?
}

bool has_data(serial_port* p)
{
    return p->receiveFlag;
}

bool has_timed_out(serial_port* p)
{
    return p->timeoutFlag;
}
bool is_serial_finished(serial_port* p)
{
    return has_data(p) || has_timed_out(p); 
}

bool serial_check(serial_port* p)
{
    if(is_serial_finished(p) && p->response_handler != NULL)
    {
       p->response_handler(p)
       p-> response_handler = NULL;
       return true;
    }
    return false;
}

void send(serial_port* p, char* message, char* response)
{
    p->response_handler=NULL;
    send_serial(p, message);
    while(!is_serial_finished(p));
    receive_serial(p, response);
}

void sendAsync(serial_port* p, char* message, serial_on_recived_proc handler, void* context)
{
    p->response_handler = handler;
    p->context = context;
    send_serial(p, message);
}

void pow_response(serial_port* p)
{
    // could pass a pointer to a struct, or anything depending on what you want to do
    char* r = (char*)p->context;  
    receive_serial(p, r);
    // do stuff with the pow response
}

typedef struct
{
   char text[100];       
   int x;
   bool has_result;
} bang_t;

void bang_parse(bang_t* bang)
{
   bang->x = atoi(bang->text);
}

void bang_response(serial_port* p)
{
    bang_t* bang = (bang_t*)p->context;  
    receive_serial(p, bang->text);
    bang_parse(bang);
    bang->has_result=true;
}

void myFunc();
{
    char response[100];
    char pow[100];
    bang_t bang1;
    bang_t bang2;
    serial_port p; //
    int state = 1;
    // whatever you need to do to set the serial port

    // sends and blocks till a response/timeout
    send(&p, "Hello", response);
    // do what you like with the response

    // alternately, lets do an async send...
    sendAsync(&p, "Pow", pow_response, pow);       

    while(true)
    {
        // non block check, will process the response when it arrives               
        if(serial_check(p))
            {
              // it has responded to something, we can send something else...

              // using a very simple state machine, work out what to send next.
              // in practice I'd use enum for states, and functions for managing state
              // transitions, but for this example I'm just using an int which
              // I just increment to move to the next state
              switch(state)
              {
              case 1: 
                 // bang1 is the context, and will receive the data
                 sendAsync(&p, "Bang1", bang_response, &bang1);
                 state++; 
                 break;
              case 2:
                 // now bang2 is the context and will get the data...
                 sendAsync(&p, "Bang2", bang_response, &bang2);
                 state++; 
                 break;
              default:
                 //nothing more to send....
                 break;
              }
            }
        // do other stuff you want to do in parallel
    }
};
Run Code Online (Sandbox Code Playgroud)