STM32 RS485通信应用

前言:本工程代码在STM32F407ZET6开发板上测试通过。

1. RS485基础知识

a. 485接口

485(一般称作RS485/EIA-485)是隶属于OSI模型物理层的电气特性规定为2线,半双工,多点通信的标准。它的电气特性和RS-232大不一样。用缆线两端的电压差值来表示传递信号。RS485仅仅规定了接受端和发送端的电气特性。它没有规定或推荐任何数据协议。

b. RS485的特点

① 接口电平低,不易损坏芯片。RS485的电气特性:逻辑“1”以两线间的电压差为+(2~6)V 表示;辑“0”以两线间的电压差为-(2~6)V表示。接口信号电平比RS232降低了,不易损坏 接口电路的芯片。
② 传输速率高。10米时,RS485的数据最高传输速率可达35Mbps,在1200m时,传输速度可达100Kbps。
③ 抗干扰能力强。RS485接口是采用平衡驱动器和差分接收器的组合,抗共模干 扰能力增强,即抗噪声干扰性好。
④传输距离远,支持节点多。RS485总线最长可以传输1200m以上(速率≤100Kbps)一般最大支持32个节点,如果使用特制的485芯片,可以达到128个或者256个节点,最大的可以支持到400个节点。

c. RS485的接口原理

RS485推荐使用在点对点网络中,线型,总线型,不能是星型,环型网络。理想情况下RS485需要2个匹配电阻,其阻值要求等于传输电缆的特性阻抗(一般为120Ω)。没有特性阻抗的话,当所有的设备都静止或者没有能量的时候就会产生噪声,而且线移需要双端的电压差。没有终接电阻的话,会使得较快速的发送端产生多个数据信号的边缘,导致数据传输出错。

485推荐的连接方式:
STM32 RS485通信应用_第1张图片
在上面的连接中,如果需要添加匹配电阻,我们一般在总线的起止端加入,也就是主机和设备4上面各加一个120Ω的匹配电阻。

SP3485内部结构图:
2
图中:
A、B总线接口,用于连接485总线。RO是接收输出端,DI是发送数据收入端,RE是接收使能信号(低电平有效),DE是发送使能信号(高电平有效)。

SP3485硬件连接:
这里写图片描述
注意:
R55和R56是两个偏置电阻,用来保证总线空闲时,AB之间的电压差都会大约200mV,避免总线空闲时压差不定逻辑混乱。

2. RS485串口编程

a. 编程思路

使用RS485实现两个MCU之间的通信,把接收到的数据通过串口助手显示在超级终端上。首先对Usart1和Usart2进行初始化,Usart1负责与串口助手通信,Usart2与RS485连接进行两个MCU之间的通信。然后编写发送和接收函数,接收函数在Usart2的中断服务函数中实现。最后把接收到的数据和必要的提示信息发送到超级终端上显示。

b. 功能模块代码

①串口初始化

void Uart1_Init(void)
{
    //USART1 初始化
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);        //开启GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);       //开启USART1时钟

    //串口1对应引脚复用映射
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);     //GPIOA9复用为USART1
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);    //GPIOA10复用为USART1

    //USART1端口配置
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;     //GPIOA9,GPIOA10
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                //复用功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;           //速度50MHz
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;              //推挽复用输出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;                //上拉
    GPIO_Init(GPIOA,&GPIO_InitStructure);                       //初始化PA9,PA10

    //USART1 端口配置
    USART_InitStructure.USART_BaudRate      = 115200;
    USART_InitStructure.USART_WordLength    = USART_WordLength_8b;
    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(USART1, &USART_InitStructure); 

    USART_Cmd(USART1, ENABLE);  //使能串口1

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);          //开启相关中断

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

    //Usart1 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;       
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; 
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =3;       
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;         
    NVIC_Init(&NVIC_InitStructure);                         
}

void Uart2_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);        
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);       

     //串口2对应引脚复用映射
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2);     
    GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2);     

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3;  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;                        
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;               
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;                  
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;                    
    GPIO_Init(GPIOA,&GPIO_InitStructure);                                   

    //USART2 端口配置
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    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);  

    USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);      

    //Usart2 NVIC 配置
    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;   
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority =2;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;     
    NVIC_Init(&NVIC_InitStructure); 
}

②接收数据

void USART2_IRQHandler(void)
{
    static u32 rx_i=0;
   if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
   {     
        USART_ClearITPendingBit(USART2,USART_IT_RXNE); //清除标志位
        rx_buf[rx_i++] = USART_ReceiveData(USART2);    //rx_buf是在main.c定义的全局变量
        while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET); 
   }
   rx_flag = 1;
}

③RS485初始化
(SP3485的RE,DE引脚与MCU的PG8引脚相连接)

void Rs485_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG, ENABLE);   

    GPIO_InitStruct.GPIO_Pin   = GPIO_Pin_8;        
    GPIO_InitStruct.GPIO_Mode  = GPIO_Mode_OUT; 
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; 
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; 
    GPIO_InitStruct.GPIO_PuPd  = GPIO_PuPd_UP;  
    GPIO_Init(GPIOG, &GPIO_InitStruct);     

//  RS485_TX_EN = 0; //默认为接收模式
}

④主函数

int main(void)
{
    char *tx_buf = "I believe I can fly!";
    u8 len;
    Led_Init();
    Key_Init();
    Systick_Init();
    Uart1_Init();
    Uart2_Init();
    Rs485_Init();

    printf("Usart test succeeded!\r\n");
    while(1)
    {
        if(!KEY0)            //KEY1按键按下
        {                    
            delay_ms(10);    //消抖动
            if(!KEY0)
            {
                while(!KEY0);
                RS485_TX_EN = 1; //发送模式,RS485_TX_EN是自定义的一个宏,即对PG8进行置位复位
                len = strlen(tx_buf);
                while(len--)
                {
                    USART_SendData(USART2, *tx_buf++);
                    while(USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
                }
                printf("Send data succeeded!\r\n"); //printf函数已经重定义
            }
        }
        if(!KEY1)              //KEY1按键按下
        {
            delay_ms(10);      //消抖动
            if(!KEY1)          //等待按键松开
            {
                while(!KEY1);
                RS485_TX_EN = 0;    //接收模式
                if(rx_flag)
                {
                    rx_flag = 0; //清除标志
                    printf("Receive data: %s\r\n", rx_buf);
                }
            }
        }
    }
}

c. 工程说明

把程序分别下载到两个MCU后,一方按下发送键后,另一方按下接收键即可接收数据。

效果图
STM32 RS485通信应用_第2张图片

STM32 RS485通信应用_第3张图片

STM32 RS485通信应用_第4张图片

STM32 RS485通信应用_第5张图片

现场调试图
STM32 RS485通信应用_第6张图片

说明:本次调试使用的是两个不同的工程,所以双方发送的数据和提示信息不一样。其实也可以两个MCU用同一个程序,其发送的数据就会一致。

若需要下载本工程完整代码,可以点击一下连接:
http://download.csdn.net/download/xiebaocheng12138/10019199

你可能感兴趣的:(stm32)