【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);
}
  • 3
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值