串口解析 void serial_init() - beginner-lei/GRBL GitHub Wiki
serial.c -低层次功能函数通过串口发送和收到字节,Grbl的一部分
#ifndef serial_h #define serial_h
#ifndef RX_BUFFER_SIZE
#define RX_BUFFER_SIZE 128 //接收缓冲尺寸 #endif #ifndef TX_BUFFER_SIZE
#define TX_BUFFER_SIZE 64 //发送数据缓存尺寸 #endif
#define SERIAL_NO_DATA 0xff //串口没有数据
#ifdef ENABLE_XONXOFF //XON/XOFF 是一种流控制协议(通信速率匹配协议)
#define RX_BUFFER_FULL 96 // XOFF high watermark
#define RX_BUFFER_LOW 64 // XON low watermark
#define SEND_XOFF 1
#define SEND_XON 2
#define XOFF_SENT 3
#define XON_SENT 4
#define XOFF_CHAR 0x13
#define XON_CHAR 0x11 #endif
void serial_init(); //初始化串口
// Writes one byte to the TX serial buffer. Called by main program. //将一个字节写入TX缓冲区。由主程序调用。
void serial_write(uint8_t data);
// Fetches the first byte in the serial read buffer. Called by main program. //在读取缓冲区读取一个字节数据。由主程序调用。
uint8_t serial_read();
// Reset and empty data in read buffer. Used by e-stop and reset. //重置和清除缓冲区中的数据。通过紧急停止和重启调用。
void serial_reset_read_buffer();
// Returns the number of bytes used in the RX serial buffer. // 在RX串口缓冲器读取数据,函数返回字节数
uint8_t serial_get_rx_buffer_count();
// Returns the number of bytes used in the TX serial buffer. // NOTE: Not used except for debugging and ensuring no TX bottlenecks.
//返回TX系列中使用的缓冲区的字节数。 //注意:不习惯除了调试,确保没有TX瓶颈。 uint8_t serial_get_tx_buffer_count();
#endif
serial.c
uint8_t rx_buffer[RX_BUFFER_SIZE];
uint8_t rx_buffer_head = 0;
uint8_t rx_buffer_tail = 0;
uint8_t tx_buffer[TX_BUFFER_SIZE];
uint8_t tx_buffer_head = 0;
volatile uint8_t tx_buffer_tail = 0;
目测是环形队列格式,输出 和 接收 都是 采用环形队列,感觉会有一定难度
//返回在RX缓冲的字节数。这替换一个典型的字节计数器,以防止中断和主要程序写入柜台在同一时间。
uint8_t serial_get_rx_buffer_count() { uint8_t rtail = serial_rx_buffer_tail; // Copy to limit multiple calls to volatile
if (serial_rx_buffer_head >= rtail) { return(serial_rx_buffer_head-rtail); }
return (RX_BUFFER_SIZE - (rtail-serial_rx_buffer_head)); }
接下来是初始化串口波特率,配置RX,TX,中断
void serial_init() { // Set baud rate
uint16_t UBRR0_value = ((F_CPU / (4L * BAUD_RATE)) - 1)/2;
UCSR0A |= (1 << U2X0); // baud doubler on for high baud rates, i.e. 115200
UBRR0H = UBRR0_value >> 8;
UBRR0L = UBRR0_value; //设置波特率
// enable rx and tx
UCSR0B |= 1<<RXEN0; //设置引脚 UCSR0B |= 1<<TXEN0;
// enable interrupt on complete reception of a byte
UCSR0B |= 1<<RXCIE0; //设置中断
// defaults to 8-bit, no parity, 1 stop bit }
串口写数据
void serial_write(uint8_t data) {
// Calculate next head uint8_t next_head = serial_tx_buffer_head + 1;
if (next_head == TX_BUFFER_SIZE) { next_head = 0; } //计算下一个头,当next_head == TX_BUFFER_SIZE 时,表面走了一圈了,要再 从 0 开始。
// Wait until there is space in the buffer 等到有缓冲的空间
while (next_head == serial_tx_buffer_tail) { // TODO: Restructure st_prep_buffer() calls to be executed here during a long print.
if (sys.rt_exec_state & EXEC_RESET) { return; } // Only check for abort to avoid an endless loop. 只检查中止,以避免无限循环。
} //当next_head == tx_buffer_tail 时,说明环形队列中已经没有空间了,在这里循环等待。循环内部判断 sys状态,避免死循环。
// Store data and advance head 存储数据并推进
serial_tx_buffer[serial_tx_buffer_head] = data;
serial_tx_buffer_head = next_head;
// Enable Data Register Empty Interrupt to make sure tx-streaming is running
//使数据寄存器清中断,确保tx-streaming正在运行
UCSR0B |= (1 << UDRIE0); }
串口发送中断
1.临时tx_buffer_tail
2.Send a byte from the buffer();
3.尾位置更新
4.关闭数据寄存器空中断停止tx-streaming如果转移到此结束
ISR(SERIAL_UDRE) { uint8_t tail = serial_tx_buffer_tail; // Temporary serial_tx_buffer_tail (to optimize for volatile)
//临时serial_tx_buffer_tail(优化) #ifdef ENABLE_XONXOFF //如果流控 if (flow_ctrl == SEND_XOFF) { UDR0 = XOFF_CHAR; flow_ctrl = XOFF_SENT; } else if (flow_ctrl == SEND_XON) { UDR0 = XON_CHAR; flow_ctrl = XON_SENT; } else #endif { // Send a byte from the buffer 从缓冲区发送数据 UDR0 = serial_tx_buffer[tail];
// Update tail position 改变尾部位置
tail++;
if (tail == TX_BUFFER_SIZE) { tail = 0; }
serial_tx_buffer_tail = tail;
}
// Turn off Data Register Empty Interrupt to stop tx-streaming if this concludes the transfer
// 关闭数据寄存器空中断停止tx-streaming如果转移到此结束 // 没有数据了,设置寄存器UDRIEn:USART数据寄存器空中使能 if (tail == serial_tx_buffer_head) { UCSR0B &= ~(1 << UDRIE0); }
}
缓存器读数据
uint8_t serial_read() { uint8_t tail = serial_rx_buffer_tail; // Temporary serial_rx_buffer_tail (to optimize for volatile) if (serial_rx_buffer_head == tail) { return SERIAL_NO_DATA; } else { uint8_t data = serial_rx_buffer[tail];
tail++;
if (tail == RX_BUFFER_SIZE) { tail = 0; }
serial_rx_buffer_tail = tail;
#ifdef ENABLE_XONXOFF
if ((serial_get_rx_buffer_count() < RX_BUFFER_LOW) && flow_ctrl == XOFF_SENT) {
flow_ctrl = SEND_XON;
UCSR0B |= (1 << UDRIE0); // Force TX
}
#endif
return data;
} }
串口中断接收 ISR(SERIAL_RX) { uint8_t data = UDR0; //接收数据 uint8_t next_head;
// Pick off realtime command characters directly from the serial stream. These characters are
// not passed into the buffer, but these set system state flag bits for realtime execution.
//选择了直接从串行实时命令字符流。 从串口流去除实时命令控制符,这些字符是系统实时执行的状态标志位
switch (data) {
case CMD_STATUS_REPORT: bit_true_atomic(sys.rt_exec_state, EXEC_STATUS_REPORT); break; // Set as true接收数据为执行开始报告就设置为执行开始报告状态
case CMD_CYCLE_START: bit_true_atomic(sys.rt_exec_state, EXEC_CYCLE_START); break; // Set as true 状态改为循环开始状态
case CMD_FEED_HOLD: bit_true_atomic(sys.rt_exec_state, EXEC_FEED_HOLD); break; // Set as true 执行进给
case CMD_SAFETY_DOOR: bit_true_atomic(sys.rt_exec_state, EXEC_SAFETY_DOOR); break; // Set as true 安全门状态
case CMD_RESET: mc_reset(); break; // Call motion control reset routine. 运动控制复位程序。
default: // Write character to buffer 写字符缓冲区
next_head = serial_rx_buffer_head + 1;
if (next_head == RX_BUFFER_SIZE) { next_head = 0; }
// Write data to buffer unless it is full. 写数据到缓冲区,除非缓冲区满了
if (next_head != serial_rx_buffer_tail) {
serial_rx_buffer[serial_rx_buffer_head] = data;
serial_rx_buffer_head = next_head;
#ifdef ENABLE_XONXOFF //串口流控
if ((serial_get_rx_buffer_count() >= RX_BUFFER_FULL) && flow_ctrl == XON_SENT) {
flow_ctrl = SEND_XOFF;
UCSR0B |= (1 << UDRIE0); // Force TX
}
#endif
}
//TODO: else alarm on overflow?
} }
复位缓冲区 void serial_reset_read_buffer() { serial_rx_buffer_tail = serial_rx_buffer_head;
#ifdef ENABLE_XONXOFF flow_ctrl = XON_SENT; #endif }