串口解析 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 }