源代码参考的是grblHAL库. 目前是2024年09月6日. 具体版本不知. 至少是1个月前的版本.

串口命令数据接收流程 代码调用堆栈分析

main.c -> main() 
{
	
	grbl_enter() //	grbllib.c  :line 118 
	{
		
		protocol_main_loop() // grbllib.c  :line 339 
		{
			
			while((c = hal.stream.read()) != SERIAL_NO_DATA) //protocol.c :line 212  ->
			{	
				//  ..... 循环读取串口数据
			}
		}
	}
}

hal.stream 是何时指向串口的?
hal.stream.read() 函数是如何从串口读取数据的?

hal.stream 是个高级抽象层, 这个流可以是多种物理接口的数据来源.
通过搜索可知hal.stream.read 在 arm-driver/serial.c文件中定义.

hal.stream.read = serialGetC;

在arm-driver/serial.c文件中serialGetC 函数定义如下.

static int16_t serialGetC (void)
{
    int16_t data;
    uint_fast16_t bptr = rxbuffer.tail;

    if(bptr == rxbuffer.head)
        return -1; // no data available else EOF

    data = rxbuffer.data[bptr++];                   // Get next character, increment tmp pointer
    rxbuffer.tail = bptr & (RX_BUFFER_SIZE - 1);    // and update pointer

    return data;
}

由此代码可知 数据存储在 rxbuffer 中.
既然是buffer说明是有入口将数据放进去. 而且这个rxbuffer是个循环队列. 有两个指针一个是指向队首的head指针, 一个是指向队尾的tail指针
分析代码发现数据是通过串口中断处理函数 uart_interrupt_handler 进行的数据填入.

static void uart_interrupt_handler (void)
{
    uint_fast16_t bptr;
    int32_t data;
    uint32_t iflags;

// 应取消并实现   iflags = UART_GET_IRQSSTATE(); // Get UART interrupt flags.

    if(iflags & UART_IRQ_TX) {

        bptr = txbuffer.tail;

        if(txbuffer.head != bptr) {

            //应取消并实现 UART_TX_WRITE(UARTCH, txbuffer.data[bptr++]);    // Put character in TXT register
            bptr &= (TX_BUFFER_SIZE - 1);                       // and update tmp tail pointer.

            txbuffer.tail = bptr;                               //  Update tail pointer.

            if(bptr == txbuffer.head)                           // Disable TX interrups
               //应取消并实现 UART_TX_IRQ_DISABLE();                        // when TX buffer empty.
        }
    }

    if(iflags & (UART_IRQ_RX)) {

        bptr = (rxbuffer.head + 1) & (RX_BUFFER_SIZE - 1);  // Get next head pointer.

        if(bptr == rxbuffer.tail) {                         // If buffer full
            rxbuffer.overflow = 1;                          // flag overflow.
            //应取消并实现 UART_RX_IRQ_DISABLE(); Clear RX interrupt, may be done by a dummy read of the RX register
        } else {
            //应取消并实现 data = UART_GET(); Read received character to data varable, clear RX interrupt if not done automatically by read.
            if(data == CMD_TOOL_ACK && !rxbuffer.backup) {  // If tool change acknowledged
                stream_rx_backup(&rxbuf);                   // save current RX buffer     
                hal.stream.read = serialGetC;               // and restore normal input.
            } else if(!hal.stream.enqueue_realtime_command((char)data)) {
                rxbuffer.data[rxbuffer.head] = (char)data;  // Add data to buffer.
                rxbuffer.head = bptr;                       // and update pointer.
            }
        }
    }
}

那么只需要修改这个uart_interrupt_handler 函数, 改成对应的串口中断处理函数, 并从串口处读取数据即可. 这个函数中 //应取消并实现的部分实现它即可.

至此, 从串口读取数据并进入循环的代码第一阶段已清晰.