232通讯
全双工
单片机TXD接232的T/R-
单片机RXD接232的RXD+
485通讯
半双工
485数据线(485+) 接 DB9 DCO
485数据线(485-) 接 DB9 RXD
422通讯
全双工
主机发送端(T/R-)接从机接收端(RXD-)
主机发送端(T/R+)接从机接收端(RXD+)
主机接收端(R-)接从机发送端(TXD-)
主机接收端(R+)接从机发送端(TXD+)
串口通讯实战经验
1、我们在串口收发数据的时候尽可能的要使用DMA的方式去接受和发送数据,如果采用中断的方式收发将会非常占用CPU的资源
2、在采用DMA收发的时候要去串口中断里检测串口是否接受缓冲区/发送缓冲区为空,判定数据是否发送完成,因为DMA传输完成不一定串口缓冲区发送完成!
3、先使用单片机串口将读到的数据压入缓冲区,压入完成后,将数据进行提取放入环形缓冲区,之后在从环形缓冲区中读取数据进行解析等操作!
这个过程我们分析一下,假设5ms接收一次数据,那我们就需要5ms读取解析一次数据,在串口中使用DMA将数据压入数组,将数组中的数据通过读取到的帧头,累加和判断该数据是否完整,如果完整将这段数据进行解析(比如从这段数据提取我们要的角度速度信息),将解析后的数据压入环形队列进行保存,之后我们就可以从环形队列中获取数据进行使用
1、我们如何知道DMA压入的数组中是否有数据
我们可以采用结构体的方式
数组,数据量,这两个组合在一起就可以通过数据量这个成员获取数组中是否有我们想要的数据,每进入一次空闲中断,我们就将DMA转存的数据量累加,解析完成后再将数据量清0,DMA压入数据前要获取这个这个数据量是否为0,否则下一次DMA压入数据就不能从0开始压。
串口中断种类
以GD32F4为例
\arg USART_INT_PERR: parity error interrupt,奇偶校验错误中断
\arg USART_INT_TBE: transmitter buffer empty interrupt,发送缓冲区空中断
\arg USART_INT_TC: transmission complete interrupt,传输完成中断
\arg USART_INT_RBNE: rread data buffer not empty interrupt and flag,读取数据缓冲区非空中断和标志\arg USART_INT_FLAG_RBNE_ORERR: read data buffer not empty interrupt and overrun error flag,读取缓冲区非空中断和溢出错误标志
\arg USART_INT_IDLE: IDLE line detected interrupt,总线空闲中断
\arg USART_INT_LBD: LIN break detected interrupt,LIN中断检测中断
\arg USART_INT_ERR: error interrupt,错误中断
\arg USART_INT_CTS: CTS interrupt,CTS中断
\arg USART_INT_RT: interrupt enable bit of receive timeout event,接收超时事件的中断启用位
\arg USART_INT_EB: interrupt enable bit of end of block event,中断启用块结束事件位
空闲中断
空闲的定义是总线上在一个字节的时间内没有再接收到数据,空闲中断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收到数据的时候发生的。针对的是接收
/**
***********************************************************
* @brief 串口引脚初始化
* @param
* @return
***********************************************************
*/
static void drv_uart_gpio_init(void)
{
/* connect port to USARTx_Rx */
rcu_periph_clock_enable(s_uart0_info.rcu_gpio);
gpio_af_set(s_uart0_info.gpio, s_uart0_info.af_num, s_uart0_info.rx_pin);
gpio_mode_set(s_uart0_info.gpio, GPIO_MODE_AF, GPIO_PUPD_PULLUP, s_uart0_info.rx_pin);
gpio_output_options_set(s_uart0_info.gpio, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, s_uart0_info.rx_pin);rcu_periph_clock_enable(s_uart0_info.rcu_gpio_tx);
gpio_af_set(s_uart0_info.gpio_tx, s_uart0_info.af_num, s_uart0_info.tx_pin);
gpio_mode_set(s_uart0_info.gpio_tx, GPIO_MODE_AF, GPIO_PUPD_PULLUP, s_uart0_info.tx_pin);
gpio_output_options_set(s_uart0_info.gpio_tx, GPIO_OTYPE_PP, GPIO_OSPEED_50MHZ, s_uart0_info.tx_pin);/* configure USART Rx as alternate function push-pull */
}/**
***********************************************************
* @brief 串口配置初始化
* @param
* @return
***********************************************************
*/
static void dev_uart_config_init(uint32_t baudRate)
{
/* USART configure */
rcu_periph_clock_enable(s_uart0_info.rcu_uart);
usart_deinit(s_uart0_info.uart_num);
usart_baudrate_set(s_uart0_info.uart_num,baudRate);usart_receive_config(s_uart0_info.uart_num, USART_RECEIVE_ENABLE);
usart_transmit_config(s_uart0_info.uart_num, USART_TRANSMIT_ENABLE);
/*DMA USART configure */
usart_interrupt_enable(s_uart0_info.uart_num, USART_INT_TC); //使能传输完成中断
usart_interrupt_enable(s_uart0_info.uart_num, USART_INT_IDLE); //使能空闲中断
usart_interrupt_flag_clear( s_uart0_info.uart_num, USART_INT_FLAG_TC );
usart_enable(s_uart0_info.uart_num);nvic_irq_enable(s_uart0_info.irq, 0, 0);
}
在中断里去判断这些中断结合DMA会使得我们更加方便,比如DMA接收,接收完成后就会进入空闲中断,我们可以准备下一次的接收,DMA发送数据可以在串口发送完成
串口传输完成中断
串口发送缓冲区中的数据已经全部发送完毕,且发送队列为空,会触发此中断
读取数据缓冲区非空和溢出错误中断
这个就是在中断中一个字节一个字节的接收数据
野火串口调试助手使用记录
1、如果仅仅只是想要显示波形只需要改变发送指令的串口数据发送函数即可
2、串口数据发送频率要小于200HZ否则会出现波形到0的现象
最近发现野火串口调试助手并不好用,看到网上推荐这个软件Serialplot,在此记录使用的方式方法
Serialplot串口使用调试记录
软件下载地址:
简单易用的开源串口波形软件SerialPlot,支持各种数据格式,直接串口打印,支持多通道 - 开发环境 - 硬汉嵌入式论坛 - Powered by Discuz!
软件配置及其说明
主要看软件左下角前两个配置
串口配置
软件配置,我们使用ASCII模式,通道数量设置为自动,通道数据用逗号隔开
这样设置之后我们只需要在发送数据的时候用逗号隔开就会自动生成不同的通道曲线
软件发送编写
我们想要发送ASCII数据,我们第一个想到的就是printf函数,这个函数很方便的就帮我们将数据进行打印
printf函数
int fputc(int ch, FILE *f)
{
usart_data_transmit(s_uart_info.uart_num, (uint8_t)ch);
while (RESET == usart_flag_get(s_uart_info.uart_num, USART_FLAG_TBE));
return ch;
}
printf("%d,%d\n",Temp1,Temp2);
将发送的数据转换成ASCII保存在数组缓冲区,再进行发送
sprintf()函数的功能,将数据以ASII类型保存在指定缓冲区
//将Temp1,Temp2整形数据以ASCII形式保存在s_rcv_data_buf缓冲区,并保存长度
TranLong = sprintf((char*)s_rcv_data_buf,"%d,%d\n",Temp1,Temp2);
//串口发送数据
DebugUSART0TransmitData(s_rcv_data_buf,TranLong);
两种发送都可以,但是如果使用485串口发送最好选择第二种,485串口发送需要延时才可以进入接收模式,printf函数并不适合延时情况。
串口使用注意事项
1、要想在单片机中运用printf(),sprintf(),必须使用同一个串口外设,如果不同会导致单片机无法运形到main()!!!
2、要使用printf函数一定要看自己keil配置有没有勾选微库,或者添加固件,二选一!