【STM32-RS485串口通信踩过的坑】

一、串口初始化 (主控芯片用的GD32f303rct6)

void usart_init()
{
	/* 略 */
}

/*****************  发送一个字节 **********************/
/* 单独调用时要记得先使能485发送引脚,发送后切为接收模式 */
void usart_sendbyte(uint32_t usart_periph, uint8_t ch)
{
	/* 发送一个字节数据到USART */
	usart_data_transmit(usart_periph, ch);	
	/* 等待发送数据寄存器为空 */
	while (usart_flag_get(usart_periph, USART_FLAG_TBE) == RESET);
}

/*****************  发送指定长度的字符串 **********************/
void rs485_send_data(uint8_t *str,uint32_t strlen )
{
	unsigned int k=0;

	rs485_mode_set(1);	//切换为发送
    do 
    {
        usart_sendbyte(USART0, *(str + k));
        k++;
    } while(k < strlen);
		
	///*yehuo de 加短暂延时,保证485发送数据完毕*/
	//Delay(0xFFF);  /* 短延时没用, 导致最后一个字节为0xff, 长延时不能接受 */
	/* yuanzi de 等待发送完成 */
	while (usart_flag_get(USART0, USART_FLAG_TC) == RESET);  /* 有用 */
		
	rs485_mode_set(0);	//切换为接收模式
}

/* _485延时等待,切换收发模式时等待硬件完成 */
void rs485_delay()
{
	__IO uint32_t count = 30;
	for(;count!=0;count--);
}

/* 控制485模块收发模式 0:recv, 1:send*/
void rs485_mode_set(uint8_t mode)
{
	rs485_delay();
	if (mode)
		gpio_bit_reset(GPIOA, GPIO_PIN_5);  /* PA5是rs485收发模式控制引脚 */
	else 
		gpio_bit_set(GPIOA, GPIO_PIN_5);	
	rs485_delay();
}

二、printf重定向到串口

/* retarget the C library printf function to the USART */
int fputc(int ch, FILE *f)
{
	//rs485_mode_set(1);  不能在里面切模式, 有延时
	usart_data_transmit(USART0, (uint8_t)ch);
    while(RESET == usart_flag_get(USART0, USART_FLAG_TBE));
    //rs485_mode_set(0);
    return ch;
}

三、踩到的坑:

1. RS485使用串口输出任意长度字符串, 最后一个字节固定为FF(其他的字节偶尔乱码)

原因显然是最后一个字节没发出去, 网络上的解决方法:

  1. 多发一个空字符.
  2. 发完切模式前加延时, 1ms. 本人实测不同波特率需要不同的延时(1ms), 延时较长并且其他的字符也不稳定.

实际解决方法: 等发送完成标志

while (usart_flag_get(USART0, USART_FLAG_TC) == RESET);  /* 有用 */
2. printf函数规律性乱码

其中一个原因同上, 另一个是: printf重定向问题, 不应该在int fputc(int ch, FILE *f)里面切换RS485收发器工作模式, printf调用此函数每发送一个字符, 会调用4次rs485_dealy(). 解决方法:

  1. 不用printf()函数
  2. 在printf()函数外面切换RS485收发器工作模式 (直观但啰嗦)
rs485_mode_set(1);
printf();  /* 加的log */
rs485_mode_set(0);
  1. 将上面3行代码封装成一个函数, 参数与printf()的一样, 貌似很难实现可变长度参数的传递, 挖坑待填: 可以参考rt-thread的rt_printk()实现原理.
  • 2022.1.8更新:
#define printk(fmt, args...)	do{\
								rs485_mode_set(1);\
								printf(fmt, ##args);\
								while (usart_flag_get(USART0, USART_FLAG_TC) == RESET);\
								rs485_mode_set(0);\
								} while (0)
  • 1.26更新(未测试是否可用)
void u3_printf(const char* fmt, ...)  
{  
	u16 i = 0;
	u8 buffer[256];
	
	va_list args;
	va_start(args, fmt);
	vsprintf(buffer, 255, fmt, args);

	while ((i < 256) && (buffer[i]))
	{
		usart_sendbyte(USART3, buffer[i]); 	 //发送数据到串口3 
	}
	
	va_end(args);
}

版权声明:本文为CSDN博主「RYYB」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ponyma_/article/details/122178710

生成海报
点赞 0

RYYB

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

STM32串口发送接收数据

1.串口通信 我用的32是stm32f10x最小系统没有UART4和UART5 USART : 通用同步异步收发器 UART : 通用异步收发器 nRTS : 请求发送 nCTS : 请求接收 区别:USART指单片机的一个IO端

Matlab 与stm32单片机之间的串口通信

Matlab 与stm32单片机之间的串口通信 在我们用stm32做信号处理时,我时常需要用到Matlab对我们采集的数据进行分析,拟合。按照传统的方法,我们一般都会先将数据通过串口助手打印出来&#x