1
由于MCU内部资源限制,在应用中会出现UART接口不够用的情况,如果UART使用的波特率不太高,而且系统中的负荷不是很大的情况就可以使用端口来模拟UART的收发,下文是一个在R5F100LE(RL78)上的具体的实现方法,这里略去工程的建立过程,相应的驱动程序细节可以参考代码生成器生成的代码,这里只重点讲述代码生成器配置和软件UART的实现。
一、硬件资源准备
01一个具有外部边沿中断功能的IO口来做UART的RXD端口,下面是一个配置的例子,在这里我们使用P137(INTP0),在代码生成器里选择INTP0为下降沿中断,中断优先级可以任意设置。
02一个普通的输出口来作为UART的TXD端口。这里选择P43端口,设置端口输出高。
03两个可以产生中断、可以随时关闭启动、定时间隔可以任意设定的定时器,一个发送定时器用于发送数据,一个接收定时器用于接收数据。
将接收定时器的定时间隔初始化为1位数据的时长的一半,比如波特率2400,1位为416.6us,那么定时器设置为416.6us/2=208.3us,在这里我们使用了Timer的Channel 1
将发送定时器的定时间隔设置为1位数据时长,比如波特率2400,1位为416.6us,那么定时器设置为416.6us,在这里我们使用Timer的Channel 0。
二、UART接收功能的实现
01接收的软件实现
接收时我们需要写两个中断函数,在中断里完成数据的接收,接收完毕后设置一个标志,在主循环中根据标志来判断数据是否接收完毕。初始化时需要打开边沿中断,关闭定时器中断。
1)边沿中断函数处理
当下降沿中断到来时,在边沿中断函数里启动接收定时器,关闭边沿中断。具体程序如下:
左右滑动查看完整内容
void softuart_rece_port_fall_edge_callback(void) { softuart_rece_state = 0;//接收定时器进入次数计数清零 uart_port_intp_stop();// 关闭边沿中断 start_uart_rece_timer();//打开接收定时器中断 }
这个函数放到r_cg_intc_user.c内
2)接收定时器中断函数
每奇数次进入中断时(第1,3,5…)进行RXD端口的采样并保存数据,同时根据我们的要求进行移位操作(比如LSB或MSB,数据长度是几位),在偶数次进入中断时就直接退出,当数据接收完成后设置标志,同时关闭接收定时器中断,打开边沿中断中断,具体程序如下,这里我是用的8位数据长度和1个停止位:
左右滑动查看完整内容
void softuart_rece_timer_callback(void) { static __saddr uint8_t uartrece_shift_reg; softuart_rece_state++;//进入次数++ if(softuart_rece_state==1)//接收start位 { //check start bit if(1==get_uart_rece_port()) { //start bit error uartrece_shift_reg = 0; stop_uart_rece_timer(); uart_port_intp_start(); } } else if (softuart_rece_state<=17)//接收8位数据位 { //receive data sampling point 3,5,7,9,11,13,15,17 if(0==(softuart_rece_state&1)) return; uartrece_shift_reg >>= 1; if(1==get_uart_rece_port()) { uartrece_shift_reg |= 0x80; } } else if(softuart_rece_state>=19)//接收停止位 { //stop bit sample sampling point 19 stop_uart_rece_timer();//关闭接收定时器 uart_port_intp_start();//打开下降沿中断 if(uartrece_end_fg ==0) { uartrece_data = uartrece_shift_reg; uartrece_end_fg =1;//设置接收完毕标志 } } }
这个程序要放到r_cg_timer_user.c内
3)在主循环里调用如下函数来判断是否收到数据
左右滑动查看完整内容
uint8_t get_softuart_rece(uint8_t * buff) { if(uartrece_end_fg==1) { uartrece_end_fg =0; *buff = uartrece_data;//将数据放入接收缓冲区 return 1;//说明收到数据 } return 0; }
三、UART发送功能的实现
01发送软件的实现
发送时需要写一个发送定时器中断函数,在中断里完成数据发送,发送完成后设置一个标志,在主循环中判断,初始化时需要关闭发送定时器,中断的代码如下:
左右滑动查看完整内容
void softuart_send_callback(void) { softuart_send_state++; if(softuart_send_state==10)//发送停止位 { //sampling point 10 stop bit softuart_send_port_h(); } else if(softuart_send_state>10)//等待停止位发送完毕 { //>11 stop bit send finished softuart_send_state=0; stop_uart_send_timer(); } else//发送数据 { //samplimng point 2,3,4,5,6,7,8,9 if(0!=(uartsend_shift_reg&1)) { softuart_send_port_h(); } else { softuart_send_port_l(); } uartsend_shift_reg >>= 1; } }
这个程序要放到r_cg_timer_user.c内
当需要发送时,先将发送端口设置为低电平,然后开启发送定时器,如果正在发送返回发送错误。具体的操作代码如下:
左右滑动查看完整内容
uint8_t softuart_send(uint8_t data) { if(softuart_send_state!=0)return 0;//数据没有发送完毕 softuart_send_state = 1; DI(); softuart_send_port_l(); //发送起始位 uartsend_shift_reg=data;//将要发送的数据放到移位寄存器 start_uart_send_timer();//启动UART发送定时器 EI(); return 1; }
四、整个程序的初始化
由于相应硬件的初始化程序在R_Systeminit已经调用过了,所以我们只用调用启动程序就行了,为了方便程序的修改,用宏定义重新定义了接口部分。
左右滑动查看完整内容
#define start_uart_rece_timer() R_TAU0_Channel1_Start() #define stop_uar_rece_timer() R_TAU0_Channel1_Stop() #define uart_port_intp_start() R_INTC0_Start() #define uart_port_intp_stop() R_INTC0_Stop() #define get_uart_rece_port() P13_bit.no7 #define start_uart_send_timer() R_TAU0_Channel0_Start() #define stop_uart_send_timer() R_TAU0_Channel0_Stop() #define softuart_send_port_h() {P4_bit.no3 = 1;} #define softuart_send_port_l() {P4_bit.no3 = 0;}
如果想要修改使用的硬件资源只用修改宏定义即可。
在使用软件UART之前需要先调用一下如下初始化函数。
左右滑动查看完整内容
void softuart_int(void) { stop_uart_send_timer();//停止发送定时器 stop_uart_rece_timer();//停止接收定时器 uart_port_intp_stop(); // 边沿中断接收停止 softuart_send_port_h(); // TXD端口设置为高 uart_port_intp_start(); // 允许RXD端口的下降沿中断 softuart_send_state = 0; softuart_rece_state = 0; uartrece_end_fg = 0; }
注意
1)使用上述方法就可以实现一个软件的UART操作,但是使用过程中不能有长时间的关闭中断操作,如果有的话会影响程序的执行。
2)如果通讯波特率发生变化需要修改定时器的定时值。
审核编辑:汤梓红
全部0条评论
快来发表一下你的评论吧 !