1、设计思路
1.1、总体设计思路
设计思路:单片机A检测单片机B的功能,单片机B待检测的地方全部引出,然后再将一些功能信息发送到屏幕。单片机A和单片机B之间、单片机A和屏幕(一种可视化编辑的屏幕,规定好通信协议,利用485通信将字符串发送过去就能显示相应的内容,屏幕:TK607ID) 。
注:本文只对单片机B的485通信功能进行检测,所用的单片机芯片皆为STM32F103C8T6,用到的485通信芯片为XL3485。
1.2、软件设计思路
软件设计思路:相当于单片机B的功能是否正常要通过屏幕显示出来,而单片机A是中间的桥梁,由于485是半双工,单片机B只有收到询问指令的数据才会发送响应的数据(单片机B程序不可更改,被动);屏幕是不断的询问单片机A,单片机A收到询问后再发送响应的数据给屏幕,屏幕作出指令(主动)。由单片机A发送一个数据后等待回应,收到单片机B的数据后解析相应的功能,处理好后再发送给屏幕,中间有个时间差。
2、单片机引脚定义及连接
下面是单片机A和单片机B之间的通信:通过查找手册串口收发引脚。
黄色高亮的是串口通信收发引脚。
3、XL3485芯片
3.1引脚分布
3.2引脚定义
3.3接线方式
A、B总线接口,用于连接485总线。RO是接收输出端DI是发送数据收入端,RE是接收使能信号 (低电平有效) DE是发送使能信号(高电平有效)。所以直接将RE和DE连接一块,接PA6引脚,当输出高电平时发送数据,输出低电平时接受数据。
仅供参考,具体详见XL3485规格书。
4、遇到的问题及解决方法
遇到的问题:遇到了第一个问题,接受不到单片机B发送的数据,不知道单片机A是否发送数据。我参考了致远电子的RS485通信的详细说明,在XL3485的A总线加上一个上拉电阻、B总线加上一个下拉电阻,如下:
飞线接了上拉电阻和下拉电阻后,调试发现发送数组和接受数组有相应的值,问题解决。
我查找相关数据手册和资料发现,在485通信中,连接一个120Ω的电阻是为了实现信号的匹配和防止信号反射。这个电阻被称为终端电阻或者终端电阻器,120R电阻加在信号线末端,原因是:高频信号传输时,信号波长相对传输线较短,信号在传输线终端会形成反射波,干扰原信号,所以需要在传输线末端加终端电阻,使信号到达传输线末端后不反射,对于低频信号则不用,如下图:
5、串口通信和485通信
5.1串口通信
关于串口通信和RS485通信:串口通信很简单,有三个端口即可收发数据,发送端TX,接收端RX,以及公共地GND。发送和接受的数据实际上是一串高低电平的信号,所以要公共地GND来区别是高电平还是低电平。
5.2RS485通信
RS485通信是串口通信的一种,通常是由单片机的串口连接485通信芯片,再由485芯片和485芯片之间通信。另外RS485采用差分信号的方式,由A端和B端接受到信号后进行差值运算来识别是高电平还是低电平,这样不仅可以减少外界对信号传输的干扰,而且只需要俩根线就可以完成传输。在多设备通信,远距离通信上应用广泛。
6、代码部分
6.1串口初始化
RS485通信也是利用串口通信,第一步要对串口进行初始化,以串口2为例。
void Uart2_Init(u32 bound)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE); //使能 GPIOA 时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能 USART2
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO , ENABLE);
USART_DeInit(USART2); //复位串口1
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //USART2_TX PA.2
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //USART2_RX PA.3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure);
USART_InitStructure.USART_BaudRate = bound; //波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为 8 位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //一个停止位
USART_InitStructure.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件数据流控制
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式
USART_Init(USART2, &USART_InitStructure); //初始化串口
USART_Cmd(USART2, ENABLE); //使能串口2
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //开启接收中断
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6; //485芯片使能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化 GPIOA.1
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0; //抢占优先级 0
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; //子优先级 0
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ 通道使能
NVIC_Init(&NVIC_InitStructure); //中断优先级初始化
}
配置好响应的引脚以及参数。
6.2数据发送
u8 USART_Send_Data(u8 *buf,u16 len,USART_TypeDef* USARTx)
{
u16 t;
for(t=0;t<len;t++)
{ //循环发送数据
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET); //等待发送结束
USART_SendData(USARTx,buf[t]);
}
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC)==RESET);
return 0;
}
调用该函数,使能XL3485芯片,实现发送。
void Usart2_Printf(u8 *buf,u16 len)
{
GPIO_SetBits(GPIOA, GPIO_Pin_6); //使能发送
if(!USART_Send_Data(buf,len,USART2)) //发送完成转变成接受模式
{
GPIO_ResetBits(GPIOA, GPIO_Pin_6);
}
}
在实际发送数据时,可通过定义一个数组USART_TX_BUF_Two,再定义上述函数发送:
USART_TX_BUF_Two[0] = 1;
USART_TX_BUF_Two[1] = 0x04;
USART_TX_BUF_Two[2] = 0x00;
USART_TX_BUF_Two[3] = 0x00;
USART_TX_BUF_Two[4] = 0x00;
USART_TX_BUF_Two[5] = 0x02;
CRC16_Usart_Send = CRC16(USART_TX_BUF_Two,6); //计算CRC16校验
USART_TX_BUF_Two[6] = CRC16_Usart_Send;
USART_TX_BUF_Two[7] = CRC16_Usart_Send>>8;
Usart2_Printf(USART_TX_BUF_Two,8); //发送数据
本设计RS485通信用的是MODBUS通信协议,需要CRC检验来判断发送的数据是否正确,可采用其他不同的通信协议,也可以自己定义一种通信的协议。这样通过对函数的调用即可实现发送数据。
6.3数据的接收
数据在接收时同样定义一个数组USART_RX_BUF_Two,通过中断来接收数据(和串口通信一样):
void USART2_IRQHandler(void)
{
u8 Data;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接受中断
{
USART_ClearITPendingBit(USART2,USART_IT_RXNE);
Data = USART_ReceiveData(USART2); //(USART2->DR); //读取接收到的数据
USART_RX_BUF_Two[RxTwoNum++] = Data;
Cnt_Two = 0; //计时清零
}
}
再定义一个定时器,每隔一段时间读取寄存器的值。
void TIM3_IRQHandler(void) //TIM3中断
{
if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)
{
TIM_ClearITPendingBit(TIM3, TIM_IT_Update); //清除TIM3更新中断标志
if(RxTwoNum) //串口2定时接收,RxTwoNum不等于0时即为接收到了数据
{
if(++ Cnt_Two >= 4) //接收210/4ms
{
Cnt_Two = 0;
RxTwoNum = 0; //接收计数清零
PreRevTwoLong = RxTwoNum;
ReveceFinishTwo = 1; //串口1接收成功标志位
}
}
}
}
RS485是半双工的通信,可以根据定时结束后视为接受一次数据对标志位进行操作,接收到数据后才进行发送或者发送后才接受数据,将收发数据分开,以免数据紊乱。收到数据的时间根据需求设置中断函数。
这样每隔一段时间就可以自动接受到一次数据了。
RS485通信的收发数据和串口收发数据大同小异,要注意到RS485通信方式是半双工收发要间隔开。另外RS485在收发数据时要注意芯片的RE是接收使能信号 (低电平有效), DE是发送使能信号(高电平有效),通常可接在一起使用。文章来源:https://www.toymoban.com/news/detail-721310.html
注:-2到-6电平代表逻辑0;2到6电平代表逻辑1.文章来源地址https://www.toymoban.com/news/detail-721310.html
到了这里,关于利用RS485通信、串口收发数据的硬件连接及部分代码的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!