我在使事情复杂化吗?

我正在设计我的代码,以便通过UART从8051微型计算机与外围设备进行通信。外设响应来自主机的命令,一次只能响应一个命令。这是一个简单的发送和接收协议(protocol)。 (tx1,rx1,tx2,rx2,tx3,rx3)每个TX消息均以CR终止,每个响应均以>终止。在收到上一封邮件的回复之前,我无法发送新邮件。如果启用该选项,响应也可以在开始时回显原始TX消息(但这会导致更多流量)

一个示例消息将是:

  • TX:您好
  • RX:世界!>

  • 或带有回声选项...
  • TX:您好
  • 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;
    }
    

    优点:
  • 一切都包含在一个函数中。
  • 更容易调试

  • 缺点:
  • 该函数正在阻塞,如果外围设备从未响应,则该函数可能会挂起,因此必须实现超时。
  • 消息不能乱序接收,必须是连续的(即,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;
    }
    

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

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


  • 现在,我正在执行选项B,但是当我继续进行该项目时,我开始觉得这变得太复杂了。我很好奇你们的想法。

    谢谢!

    最佳答案

    我倾向于做这种事情,但是,Id倾向于有一个单独的串行端口“类”(struct + functions)和一个协议(protocol)类,该类位于串行端口的顶部。我一直在嵌入式系统中使用这些功能。这为您提供了两全其美的优势:阻塞同步调用和异步调用,因此您可以进行伪多任务处理。

    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
        }
    };
    

    10-06 04:04