UART ISR Tx Rx架构

我让事情复杂化了吗?

我正在设计我的代码,通过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; } 

优点:

  • 一切都包含在一个function中。
  • 更容易调试

缺点:

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

选项B采用并行方法。 将创建两个单独的function。 一个用于发送消息,另一个用于在收到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倾向于有一个单独的串口“类”(结构+函数)和一个存在于串口顶部的协议类。 我一直在嵌入式系统中使用这些。 这为您提供了两全其美,阻塞同步调用和异步调用,因此您可以伪多任务。

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

简单地说。 一个ISR例程必须非常快,所以对我来说最好的方法是拥有一个像这样的全局RXBuffer:

 #include  #include  #include  class RXBuffer { public: friend class ISR; typedef std::deque::const_iterator const_iterator; RXBuffer(); size_t size() const { m_buffer.size(); } // read from the buffer in a container in the range [first, last) template  void read(Iterator first, Iterator last, Iterator to) { // how many bytes do you want to read? size_t bytes_to_read = std::distance(first, last); if (bytes_to_read >= size()) { // read the whole buffer std::copy(begin(), end(), first); // empty the buffer m_buffer.clear(); return size(); } else { // copy the data copy(begin(), begin() + bytes_to_read, firt); // now enque the element m_buffer.erase(begin(), begon() + bytes_to_read); return bytes_to_read; } } private: void put(uint8_t data) { // check buffer overflow m_buffer.push_back(data); } const_iterator begin() const { return m_buffer.begin(); } const_iterator end() const { return m_buffer.end(); } private: std::deque m_buffer; // buffer where store data size_t m_size; // effective size of the container }; class ISR { public: ISR(RXBuffer& buffer) : m_buffer(buffer) {} // ISR Routine void operator () (uint8_t data) { m_buffer.put(data); } private: RXBuffer& m_buffer; }; RXBuffer g_uart1_rx_buffer; 

现在你有了搜索数据的ISR和RXBuffer,所以你需要一些东西来包装UART函数。 您可以按如下方式实施:

 class UART { public: UART(unsigned int uart_device, RXBuffer& rx_buffer) : m_uart(uart_device), m_buffer(rx_buffer) { } unsigned int uart_device() const { return m_uart; } // set the timeout during a read operation void timeout(unsigned ms) { m_timer.countdown(ms); } template  void read(InputIterator first, InputIterator last) { // start the timer m_timer.start(); size_t size = std::distance(first, last); size_t read_bytes = 0; while (read_bytes != size && !m_timer.is_expired()) { read_bytes += m_buffer.read(first + read_bytes, last); } if (read_bytes != size) throw std::exception("timeout"); } template  void send(OutputIterator first, OutputIterator last) { size_t size = std::distance(first, last); uart_send(m_uart, &(*first), size); } private: unsigned int m_uart; RXBuffer& m_buffer; timer m_timer; };