485基本介绍
485通信协议是一种常用的半双工串行通信协议,具有抗干扰能力强,传输距离远等特点,因此在工业及自动化领域被广泛运用。
由于485属于半双工通讯,因此不能同时进行接收和发送,需要配置485控制芯片切换为发送或者接收状态。
以ADM3485芯片为例:
依据真值表所示,传统的RS485收发电路通常将收发控制引脚RE和DE连接在一起,通过一个IO口的高低电平变化进行控制收发。
当MCU需要向外发送数据的时候,配置R/D引脚为高电平;
当MCU需要向内接收数据的时候,配置R/D引脚为低电平;
因此,抛开硬件层面的区别,在软件层面,RS485可以理解为串口通信+一组收发控制引脚。
那么,RS485的配置过程也与串口类似,只需要进行一些修改即可。
基本配置
MCU采用STM32L431RCT6
485芯片采用ADM3485
本次使用STM32L4芯片的串口USART3和串口USART1通过两路RS485进行自收发通信实验
配置PB10,PB11为USART3 ,控制引脚PC6;
配置PA9,PA10为USART1 ,控制引脚PA11;
串口初始化函数
这里我配置了一个通用的初始化函数,可以根据需求配置串口,中断,中断优先级与波特率:
/*********485-通用串口初始化函数******************/
void RS485_init(USART_TypeDef* USARTX,IRQn_Type IRQn,uint8_t priority,uint32_t baud)//串口,中断,中断优先级,波特率
{
USARTX->CR1=0;//初始化寄存器
USARTX->CR2=0;
USARTX->CR3=0;
CLEAR_BIT(USARTX->CR1,USART_CR1_OVER8);//过采样16
CLEAR_BIT(USARTX->CR1,USART_CR1_M0);//1个起始位8个数据位
CLEAR_BIT(USARTX->CR1,USART_CR1_M1);
CLEAR_BIT(USARTX->CR2,USART_CR2_STOP);//1个停止位
CLEAR_BIT(USARTX->CR1,USART_CR1_PCE);//禁用奇偶校验控制
// SET_BIT(USARTX->CR1,USART_CR1_PCE);//奇偶校验使能
// SET_BIT(USARTX->CR1,USART_CR1_PS);//奇校验
USARTX->BRR=16000000/baud;//波特率
// SET_BIT(USARTX->CR2,USART_CR2_ABRMODE);
// SET_BIT(USARTX->CR2,USART_CR2_ABREN);//自动波特率检测
NVIC_DisableIRQ(IRQn);//关中断
SET_BIT(USARTX->CR1,USART_CR1_RXNEIE);//设置RXNE中断
// SET_BIT(USARTX->CR1,USART_CR1_IDLEIE);//设置IDLE中断
// SET_BIT(USARTX->CR1,USART_CR1_TXEIE);//发送缓冲区空中断使能
// SET_BIT(USARTX->CR1,USART_CR1_TCIE);//发送完成中断使能
NVIC_SetPriority(IRQn,priority);//设置中断优先级
NVIC_EnableIRQ(IRQn);//开中断
// SET_BIT(USARTX->CR3,USART_CR3_DMAR);//开启DMA接收通道
// SET_BIT(USARTX->CR3,USART_CR3_DMAT);//开启DMA发送通道
SET_BIT(USARTX->CR1,USART_CR1_TE);//使能发送接收
SET_BIT(USARTX->CR1,USART_CR1_RE);
SET_BIT(USARTX->CR1,USART_CR1_UE);//开串口
if(USARTX==USART1)
{
RE_DE_RX1;//初始化默认为接收状态
}
else if(USARTX==USART3)
{
RE_DE_RX3;//初始化默认为接收状态
}
}
波特率配置
依据芯片编程手册:
当过采样为16(OVER8=0)时,波特率为Fck(系统总线时钟)/USARTDIV(波特率分频系数)
这里我已经将系统时钟配置为16MHZ的内部时钟HSI16:
SET_BIT(RCC->CFGR,RCC_CFGR_SWS_0);//配置时钟源为HSI16
SET_BIT(RCC->CFGR,RCC_CFGR_SW_0);
// 使能 HSI 时钟
RCC->CR |= RCC_CR_HSION;
// 等待 HSI 稳定
while (!(RCC->CR & RCC_CR_HSIRDY)) {
// 等待 HSI 稳定
};
因此直接往BRR寄存器中写入对应值即可:USARTX->BRR=16000000/baud;//波特率
RS485接收中断配置函数
本次使用基于RS485的串口USART3发送数据,串口USART1接收数据。因此,使用RXNE中断时,配置USART1中断。当接收缓冲寄存器RDR非空的时候,表示串口接收到了数据,此时触发RXNE中断,在中断函数中将RDR中的值取出并储存在预设的数组中:
//USART1串口中断服务函数
//产生RXNE中断时,将usart1接收到的数据发送到缓存接收函数RX_BUF中。
void USART1_IRQHandler()
{
//485接收数据,由USART3发送USART1接收,实现自收发
uint8_t RES;
if(READ_BIT(USART1->ISR,USART_ISR_RXNE))
{
RES=USART1->RDR &(uint16_t)0x01FF;//保存接收到的数据
if(RX_CNT<64)
{
RX_BUF[RX_CNT]=RES;//将接收到的字节每一位都存入RX_BUF里
RX_CNT++;//接收计数器+1
}
}
}
RS485发送配置函数
RS485发送数据与串口基本相同,调用该函数的时候将485控制引脚R/D配置为发送状态,通过发送完成状态寄存器TC检测TDR寄存器中的值是否已经全部发送完成。
在发送完成后,将R/D引脚配置回默认接收状态。
//485发送数据
void RS485_Send_Data(USART_TypeDef* USARTX,uint8_t *buf,uint8_t len)
{
if(USARTX==USART1)
{
RE_DE_TX1; //设置为发送模式
}
else if(USARTX==USART3)
{
RE_DE_TX3; //设置为发送模式
}
uint8_t t;
for(t=0;t<len;t++) //循环发送数据
{
while(READ_BIT(USARTX->ISR,USART_ISR_TC)==0);
USARTX->TDR=buf[t] &(uint16_t)0x01FF ;//数据放入传输数据寄存器
// USARTX->TDR=buf[t];
}
while(READ_BIT(USARTX->ISR,USART_ISR_TC)==0){};
if(USARTX==USART1)
{
RE_DE_RX1; //设置为接收模式
}
else if(USARTX==USART3)
{
RE_DE_RX3; //设置为接收模式
}
}
主函数配置
485收发引脚配置与串口配置相同,这里忽略。
开启usart1和usart3时钟:
SET_BIT(RCC->APB2ENR,RCC_APB2ENR_USART1EN);//开usart1时钟
SET_BIT(RCC->APB1ENR1,RCC_APB1ENR1_USART3EN);//开usart3时钟
开启两个RS485通讯接口,波特率配置为9600:
RS485_init(USART1,USART1_IRQn,2,9600);
RS485_init(USART3,USART3_IRQn,1,9600);
创建待发送数据数组,并填入要发送的数据:
这里配置发送两个字节数据0x04和0x02
uint8_t TCOM[2]={0x04,0x02};
发送数据:
RS485_Send_Data(USART3,TCOM,3);
最后,将接收数组RX_BUF中的两个字节数据以十进制形式分别显示在LCD液晶屏上:
可以看到接收数组中的显示数据与预先设置的发送数据一致, RS485自收发实验成功。