CH582m串口透传程序
先前在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; } }