先前在582上實現了“上位機通過串口1發送指令規定串口2和3的波特率,實現串口2和3之間的數據透傳”的功能,其實更普遍的用法就是串口間的透傳,沒有前提條件,故筆者在573上又用結構體封了一下相關變量,看上去更規整些,並加上了查詢發送與中斷發送切換的宏。接收方向上,都是中斷接收的沒有區別。
復制代碼后,編譯若報錯找不到UART3相關的函數,需要將相關庫添加到工程中一起編譯。
582也是一樣的代碼,將下面代碼的頭文件引用改成CH58x_common.h,即可。
#include "CH57x_common.h" //使用582需要講這里改成包含CH58x_common.h #define INQUIER_SEND 0 //查詢方式從另一串口發出,波特率不一致也適用 #define INTERRUPT_SEND 1 //中斷方式從另一串口發出。注意:在兩透傳串口波特率不一致時, //由於低波特率的輸出占用時間長,會影響到高波特率串口中斷數據的接收,造成丟包 #define HOW_2_SEND INQUIER_SEND uint32_t irq_status; //用於保留中斷值 uint8_t u1_rx_Buff[64]; uint8_t u3_rx_Buff[64]; struct _uart_rx_str_{ uint8_t *rx_buff; //接收緩存數組 uint8_t max_buff_size; //接收緩存數組最大長度,越界報錯 uint8_t rx_index; //已緩存數組下標 uint8_t rx_done; //在接收超時中斷中置位,表示接收完成 uint8_t trig_num; //接收FIFO觸發字節數 }; typedef struct _uart_rx_str_ uart_rx_str; uart_rx_str u1_rx_str = {.rx_buff = u1_rx_Buff, .max_buff_size = sizeof u1_rx_Buff}; //定義接收結構體並部分初始化 uart_rx_str u3_rx_str = {.rx_buff = u3_rx_Buff, .max_buff_size = sizeof u3_rx_Buff}; void u1_init(uint32_t btl) { uint32_t x; /* 配置串口1:先配置IO口模式,再配置串口 */ GPIOA_SetBits(GPIO_Pin_9); //先將PA9置位為高 GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); // RXD-配置上拉輸入 GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); // TXD-配置推挽輸出,注意要先讓IO口輸出高電平 x = 10 * GetSysClock() / 8 / btl; x = (x + 5) / 10; //整型運算中四舍五入 R16_UART1_DL = (uint16_t)x; //給波特率寄存器賦值 R8_UART1_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN; //FIFO控制寄存器配置,10h左移6位:觸發點4字節;收發FIFO數據都清空;FIFO使能 R8_UART1_LCR = RB_LCR_WORD_SZ; //線路控制寄存器配置,選擇串口數據長度 R8_UART1_IER = RB_IER_TXD_EN | RB_IER_RECV_RDY | RB_IER_LINE_STAT; //中斷使能控制器配置,TX引腳輸出使能;接收中斷使能;接收線路狀態中斷使能 R8_UART1_MCR |= RB_MCR_INT_OE; //調制解調器控制寄存器配置,允許串口的中斷請求輸出 R8_UART1_DIV = 1; //用於波特率配置,參考手冊中的公式 PFIC_EnableIRQ(UART1_IRQn); //中斷注冊 u1_rx_str.trig_num = 4; //根據R8_UART1_FCR配置設置觸發字節數 UART1_SendByte('O'); UART1_SendByte('K'); } void u3_init(uint32_t btl) //波特率數字較大時需要用32位的整型存放,比如115200 { uint32_t x; GPIOA_SetBits(GPIO_Pin_5); GPIOA_ModeCfg(GPIO_Pin_4, GPIO_ModeIN_PU); GPIOA_ModeCfg(GPIO_Pin_5, GPIO_ModeOut_PP_5mA); x = 10 * GetSysClock() / 8 / btl; x = (x + 5) / 10; R16_UART3_DL = (uint16_t)x; R8_UART3_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN; R8_UART3_LCR = RB_LCR_WORD_SZ; R8_UART3_IER = RB_IER_TXD_EN | RB_IER_RECV_RDY | RB_IER_LINE_STAT; R8_UART3_MCR |= RB_MCR_INT_OE; R8_UART3_DIV = 1; PFIC_EnableIRQ(UART3_IRQn); u3_rx_str.trig_num = 4; //根據R8_UART1_FCR配置設置觸發字節數 UART3_SendByte('O'); UART3_SendByte('K'); } /********************************************************************* * @fn main * * @brief 主函數 * * @return none */ int main() { SetSysClock(CLK_SOURCE_PLL_60MHz); u1_init(115200); u3_init(9600); while(1) { #if HOW_2_SEND == INQUIER_SEND while(u1_rx_str.rx_done) { SYS_DisableAllIrq(&irq_status); for(uint8_t i=0; i<u1_rx_str.rx_index; i++) { uint8_t j = R8_UART3_TFC; //查詢 發FIFO計數寄存器的數值,還有數據時不發送 while(j>4) //_TFC寄存器中的值最多為8,不讓FIFO溢出即可 j = R8_UART3_TFC; UART3_SendByte(u1_rx_str.rx_buff[i]); //串口3收到的數據從串口1發出 } u1_rx_str.rx_index = 0; //for循環中發完數據 u1_rx_str.rx_done = 0; //清超時發送標志 SYS_RecoverIrq(irq_status); } while(u3_rx_str.rx_done) { SYS_DisableAllIrq(&irq_status); for(uint8_t i=0; i<u3_rx_str.rx_index; i++) { uint8_t j = R8_UART1_TFC; //查詢 發FIFO計數寄存器的數值,還有數據時不發送 while(j>4) //_TFC寄存器中的值最多為8,不讓FIFO溢出即可 j = R8_UART1_TFC; UART1_SendByte(u3_rx_str.rx_buff[i]); //串口3收到的數據從串口1發出 } u3_rx_str.rx_index = 0; //for循環中發完數據 u3_rx_str.rx_done = 0; //清超時發送標志 SYS_RecoverIrq(irq_status); } #endif ; } } /********************************************************************* * @fn UART1_IRQHandler * * @brief UART1中斷函數 * * @return none */ __attribute__((interrupt("WCH-Interrupt-fast"))) __attribute__((section(".highcode"))) void UART1_IRQHandler(void) { volatile uint8_t i; switch(UART1_GetITFlag()) { case UART_II_LINE_STAT: // 線路狀態錯誤 { UART1_GetLinSTA(); break; } case UART_II_RECV_RDY: // 數據達到設置觸發點 for(i = 0; i != u1_rx_str.trig_num -1; i++) //-1保證每次接收都能進超時 { #if HOW_2_SEND == INQUIER_SEND u1_rx_str.rx_buff[u1_rx_str.rx_index] = UART1_RecvByte(); u1_rx_str.rx_index++; if(u1_rx_str.rx_index > u1_rx_str.max_buff_size) { UART1_SendByte('E'); //緩存數組越界提示,可以改良使用環形緩存,或加大緩存數組大小 return; } #endif #if HOW_2_SEND == INTERRUPT_SEND u1_rx_str.rx_buff[i] = UART1_RecvByte(); UART3_SendByte(u1_rx_str.rx_buff[i]); //從串口3轉發出去 #endif } break; case UART_II_RECV_TOUT: // 接收超時,暫時一幀數據接收完成 #if HOW_2_SEND == INQUIER_SEND i = UART1_RecvString(u1_rx_str.rx_buff + u1_rx_str.rx_index); if((u1_rx_str.rx_index +i) > u1_rx_str.max_buff_size) //數組越界訪問,可能走不到這里就代碼錯誤 { UART1_SendByte('E'); //緩存數組越界提示,可以改良使用環形緩存,或加大緩存數組大小 u1_rx_str.rx_done = 1; return; } u1_rx_str.rx_index +=i ; u1_rx_str.rx_done = 1; #endif #if HOW_2_SEND == INTERRUPT_SEND i = UART1_RecvString(u1_rx_str.rx_buff); UART3_SendString(u1_rx_str.rx_buff, i); #endif break; case UART_II_THR_EMPTY: // 發送緩存區空,可繼續發送 break; case UART_II_MODEM_CHG: // 只支持串口0 break; default: break; } } __attribute__((interrupt("WCH-Interrupt-fast"))) __attribute__((section(".highcode"))) void UART3_IRQHandler(void) { volatile uint8_t i; switch(UART3_GetITFlag()) { case UART_II_LINE_STAT: // 線路狀態錯誤 { UART3_GetLinSTA(); break; } case UART_II_RECV_RDY: // 數據達到設置觸發點 for(i = 0; i != u3_rx_str.trig_num -1; i++) //-1保證每次接收都能進超時 { #if HOW_2_SEND == INQUIER_SEND u3_rx_str.rx_buff[u3_rx_str.rx_index] = UART3_RecvByte(); u3_rx_str.rx_index++; if(u3_rx_str.rx_index > u3_rx_str.max_buff_size) { UART3_SendByte('E'); //緩存數組越界提示,可以改良使用環形緩存,或加大緩存數組大小 return; } #endif #if HOW_2_SEND == INTERRUPT_SEND u3_rx_str.rx_buff[i] = UART3_RecvByte(); UART1_SendByte(u3_rx_str.rx_buff[i]); //從串口1轉發出去 #endif } break; case UART_II_RECV_TOUT: // 接收超時,暫時一幀數據接收完成 #if HOW_2_SEND == INQUIER_SEND i = UART3_RecvString(u3_rx_str.rx_buff + u3_rx_str.rx_index); if((u3_rx_str.rx_index +i) > u3_rx_str.max_buff_size) //數組越界訪問,可能走不到這里就代碼錯誤 { UART3_SendByte('E'); //緩存數組越界提示,可以改良使用環形緩存,或加大緩存數組大小 u3_rx_str.rx_done = 1; return; } u3_rx_str.rx_index +=i ; u3_rx_str.rx_done = 1; #endif #if HOW_2_SEND == INTERRUPT_SEND i = UART3_RecvString(u3_rx_str.rx_buff); UART1_SendString(u3_rx_str.rx_buff, i); #endif break; case UART_II_THR_EMPTY: // 發送緩存區空,可繼續發送 break; case UART_II_MODEM_CHG: // 只支持串口0 break; default: break; } }
下面舊的例子代碼,不知為何直接復制到MounRiver編譯器中會多出來很多空格,用上方的新代碼不會多出空格。
----------更新線 新↑ 舊↓ 更新線--------------
參考了沁恆官網22年1月更新的CH583EVT包中的UART1例程
功能:上位機通過串口1發送指令規定串口2和3的波特率,實現串口2和3之間的數據透傳
擔心串口3的接收中斷會被串口2的發送中斷打斷而導致錯誤,故不采用在串口3的接收中斷中,立即於串口2轉發的方式(即串口3的中斷服務函數中注釋掉的兩句),而采用緩存串口3接收的數據,在主函數中轉發的方式。
實測串口2和3全部采用中斷收發不緩存數據,不會影響串口透傳,前提是兩個串口的波特率一致。如果出現某個串口波特率高過另一串口的情況,比如說一個115200,一個9600的波特率,那么在高波特率的串口接收數據,由低波特率串口轉發的過程中,會造成占用串口中斷時間過長,丟失高波特率串口包的問題。全部用中斷收發,可以減小串口3的緩存,但是在中斷使用較多的情況下不保證不會出問題。代碼相似度很高,就不貼了。
#include "CH58x_common.h" uint8_t RxBuff1[50]; uint8_t u1_i = 0; //用於判斷字符長度 uint8_t RxBuff2[50]; uint8_t RxBuff3[100]; //串口3的接收緩存,串口2向串口3透傳的數據不能溢出該數組 uint8_t trigB = 4; //FIFO的觸發字節數,可以設置00b,01b,10b,11b。本程序中用了10b,這里直接賦了4 uint8_t RxBuff1_get = 0; //判斷串口1是否接收完了一串字符,在串口1中斷中的FIFO超時判斷中置1 uint8_t u3_get_flag = 0; //判斷串口3是否接收完一串字符串,在串口3中斷中的FIFO超時判斷中置1 uint8_t baud_get = 0; //判斷是否接收到了串口1收到的波特率信息,格式 baud:XXXXX (9600/57600/115200等) uint8_t u1_get[13] = {}; //接收存放串口1收到的波特率信息,方便仿真觀察 uint8_t u3_i = 0; //記錄串口3接收到的字節數,以便后續轉發 uint32_t baud_num = 0; //存放波特率,用於初始化u2u3的函數 void u1_init() { uint32_t x; /* 配置串口1:先配置IO口模式,再配置串口 */ GPIOA_SetBits(GPIO_Pin_9); //先將PA9置位為高 GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); // RXD-配置上拉輸入 GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); // TXD-配置推挽輸出,注意要先讓IO口輸出高電平 x = 10 * GetSysClock() / 8 / 115200; //串口1定為115200波特率 x = (x + 5) / 10; //整型運算中四舍五入 R16_UART1_DL = (uint16_t)x; //給波特率寄存器賦值 R8_UART1_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN; //FIFO控制寄存器配置,10左移6位:觸發點4字節;收發FIFO數據都清空;FIFO使能 R8_UART1_LCR = RB_LCR_WORD_SZ; //線路控制寄存器配置,選擇串口數據長度 R8_UART1_IER = RB_IER_TXD_EN | RB_IER_RECV_RDY | RB_IER_LINE_STAT; //中斷使能控制器配置,TX引腳輸出使能;接收中斷使能;接收線路狀態中斷使能 R8_UART1_MCR |= RB_MCR_INT_OE; //調制解調器控制寄存器配置,允許串口的中斷請求輸出 R8_UART1_DIV = 1; //用於波特率配置,參考手冊中的公式 PFIC_EnableIRQ(UART1_IRQn); //中斷注冊 UART1_SendByte('O'); UART1_SendByte('K'); } void u2u3_init(uint32_t btl) //波特率數字較大時需要用32位的整型存放,比如115200 { uint32_t x; /* 配置串口2、3的IO口模式 */ GPIOA_SetBits(GPIO_Pin_7); GPIOA_ModeCfg(GPIO_Pin_6, GPIO_ModeIN_PU); GPIOA_ModeCfg(GPIO_Pin_7, GPIO_ModeOut_PP_5mA); GPIOA_SetBits(GPIO_Pin_5); GPIOA_ModeCfg(GPIO_Pin_4, GPIO_ModeIN_PU); GPIOA_ModeCfg(GPIO_Pin_5, GPIO_ModeOut_PP_5mA); x = 10 * GetSysClock() / 8 / btl; x = (x + 5) / 10; R16_UART2_DL = (uint16_t)x; R16_UART3_DL = (uint16_t)x; //寄存器為16位的 R8_UART2_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN; R8_UART2_LCR = RB_LCR_WORD_SZ; R8_UART2_IER = RB_IER_TXD_EN | RB_IER_RECV_RDY | RB_IER_LINE_STAT; R8_UART2_MCR |= RB_MCR_INT_OE; R8_UART2_DIV = 1; R8_UART3_FCR = (2 << 6) | RB_FCR_TX_FIFO_CLR | RB_FCR_RX_FIFO_CLR | RB_FCR_FIFO_EN; R8_UART3_LCR = RB_LCR_WORD_SZ; R8_UART3_IER = RB_IER_TXD_EN | RB_IER_RECV_RDY | RB_IER_LINE_STAT; R8_UART3_MCR |= RB_MCR_INT_OE; R8_UART3_DIV = 1; PFIC_EnableIRQ(UART2_IRQn); PFIC_EnableIRQ(UART3_IRQn); baud_get = 1; //波特率已由串口1獲得的標識 UART2_SendByte('O'); UART2_SendByte('K'); UART3_SendByte('O'); UART3_SendByte('K'); } void main() { uint8_t i,j; SetSysClock(CLK_SOURCE_PLL_60MHz); u1_init(); //串口1默認115200波特率 while(!RxBuff1_get) __nop(); //等待串口1接收指令。串口1接收到一串數據之后,開始處理數據 while(RxBuff1_get) { for(i=0; i<u1_i; i++) { j = R8_UART1_TFC; //查詢 發FIFO計數寄存器的數值,數據過多時不發送數據給FIFO while(j > 4) { j = R8_UART1_TFC; //再讀取一遍,等待FIFO有空間存放即將發送的數據。發送數據過快將導致與波特率不匹配而亂碼 } UART1_SendByte(RxBuff1[i]); //反饋上位機 u1_get[i] = RxBuff1[i]; //將RxBuff1中的指令搬到u1_get中,方便仿真觀察 } RxBuff1_get = 0; } baud_num = u1_get[5]-'0'; //將"baud:"之后的字符轉換為數字 for (i=6; i<u1_i; i++) { baud_num = baud_num *10; baud_num += u1_get[i]-'0'; } u2u3_init(baud_num); //初始化u2u3 while(baud_get) //獲得波特率,並且u2u3初始化完成之后 { __nop(); while(u3_get_flag) { for(i=0; i<u3_i; i++) { j = R8_UART2_TFC; //查詢 發FIFO計數寄存器的數值,還有數據時不發送 while(j) //作用和上面判斷FIFO數據是否過多類似,_TFC寄存器中的值最多為8,不讓FIFO溢出即可 j = R8_UART2_TFC; UART2_SendByte(RxBuff3[i]); //串口3收到的數據從串口2發出 } if(i == u3_i) { u3_i = 0; //RxBuff3的數組指針歸零 u3_get_flag = 0; //清空串口3接收到數據的標志 } } } } __INTERRUPT //硬件壓棧 __HIGH_CODE //放在ram里跑,更快 void UART1_IRQHandler(void) { volatile uint8_t i; switch(R8_UART1_IIR & RB_IIR_INT_MASK) //獲取中斷標志 { case UART_II_LINE_STAT: // 線路狀態錯誤 { UART1_GetLinSTA(); //讀取線路狀態寄存器,讀取該寄存器將自動清除中斷 break; } case UART_II_RECV_RDY: // 數據達到設置觸發點 for(i = 0; i != trigB-1; i++) { RxBuff1[u1_i] = UART1_RecvByte(); u1_i++; } break; case UART_II_RECV_TOUT: // 接收超時,暫時一幀數據接收完成。注意FIFO里要留有至少一個字符用來比較,判斷超時 i = UART1_RecvString(RxBuff1+u1_i); u1_i += i; RxBuff1_get = 1; break; case UART_II_THR_EMPTY: // 發送緩存區空,可繼續發送 break; default: break; } } __INTERRUPT //硬件壓棧 __HIGH_CODE //下方程序放在ram里跑,更快 void UART2_IRQHandler(void) { volatile uint8_t i; switch(R8_UART2_IIR & RB_IIR_INT_MASK) //獲取中斷標志 { case UART_II_LINE_STAT: // 線路狀態錯誤 { UART2_GetLinSTA(); //讀取線路狀態寄存器,讀取該寄存器將自動清除中斷 break; } case UART_II_RECV_RDY: // 數據達到設置觸發點 for(i = 0; i != trigB; i++) { RxBuff2[i] = UART2_RecvByte(); UART3_SendByte(RxBuff2[i]); //從串口3轉發出去 } break; case UART_II_RECV_TOUT: // 接收超時,暫時一幀數據接收完成 i = UART2_RecvString(RxBuff2); UART3_SendString(RxBuff2, i); break; case UART_II_THR_EMPTY: // 發送緩存區空,可繼續發送 break; default: break; } } __INTERRUPT //硬件壓棧 __HIGH_CODE //放在ram里跑,更快 void UART3_IRQHandler(void) { volatile uint8_t i; switch(R8_UART3_IIR & RB_IIR_INT_MASK) //獲取中斷標志 { case UART_II_LINE_STAT: //線路狀態錯誤 { UART3_GetLinSTA(); //讀取線路狀態寄存器,讀取該寄存器將自動清除中斷 break; } case UART_II_RECV_RDY: // 數據達到設置觸發點 for(i = 0; i != trigB-1; i++) //FIFO中留一個字節的數據,用於觸發_TOUT標識 { RxBuff3[u3_i++] = UART3_RecvByte(); //UART2_SendByte(RxBuff3[i]); //先不從串口2轉發,防止串口2的發送中斷打斷串口3的中斷服務函數 } break; case UART_II_RECV_TOUT: // 接收超時,暫時一幀數據接收完成 i = UART3_RecvString(RxBuff3+u3_i); u3_i += i; //UART2_SendString(RxBuff3, i); //先不從串口2轉發,防止串口2的發送中斷打斷串口3的中斷服務函數 u3_get_flag = 1; break; case UART_II_THR_EMPTY: // 發送緩存區空,可繼續發送 break; default: break; } }