源代码参考的是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 函数, 改成对应的串口中断处理函数, 并从串口处读取数据即可. 这个函数中 //应取消并实现的部分实现它即可.
至此, 从串口读取数据并进入循环的代码第一阶段已清晰.