• 基于STM32 的485通讯实验(f103)


    1.前言-单片机的通讯

    在单片机通讯方式多种多样的今天,基本可以划分为两类,即同步和异步通信。单片机要正常交流(即交换数据和读写命令)离不开通讯,单片机之间或者单片机与及外设之间的通讯都离不开这两类通讯。

    通讯方式的分类

     

    同步和异步通信怎么区别?

    带时钟同步信号传输的是同步传输,不带时钟同步信号的是异步传输(此时要求通讯双方同波特率)。

    下面我将通过基于stm32f103芯片以及MDK5软件进行开发485通讯实验(其实485通讯就是利用uart串口实现的),需要准备:

    一台装着MDK5软件的电脑

    ST-LInk烧录器,STM32正点原子精英开发板2套(包含TFTLCD显示屏)

    两根杜邦线

    2.485通讯简介

             要开展485通讯实验之前,485得对自己进行一次自我介绍。485通讯本质上是通过串口经过485芯片改变电压与及阻抗,内在的信息没有改变,之后通过电压电流等信号传给另一个单片机的485芯片,该芯片接至该单片进的串口。

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

    RS485的特点包括:

    1) 接口电平低,不易损坏芯片。RS485的电气特性:逻辑“1”以两线间的电压差为+(2~6)V表示;逻辑“0”以两线间的电压差为-(2~6)V表示。接口信号电平比RS232降低了,不易损坏接口电路的芯片,且该电平与TTL电平兼容,可方便与TTL 电路连接。

    2) 传输速率高。10米时,RS485的数据最高传输速率可达35Mbps,在1200m时,传输速度可达100Kbps。

    3) 抗干扰能力强。RS485接口是采用平衡驱动器和差分接收器的组合,抗共模干扰能力增强,即抗噪声干扰性好。

    4) 传输距离远,支持节点多。RS485总线最长可以传输1200m以上(速率≤100Kbps)

    一般最大支持32个节点,如果使用特制的485芯片,可以达到128个或者256个节点,最大的可以支持到400个节点。

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

     

    在上面的连接中,如果需要添加匹配电阻,我们一般在总线的起止端加入,也就是主机和设备4上面各加一个120Ω的匹配电阻。

    由于RS485具有传输距离远、传输速度快、支持节点多和抗干扰能力更强等特点,所以RS485有很广泛的应用。

    精英STM32开发板采用SP3485作为收发器,该芯片支持3.3V供电,最大传输速度可达10Mbps,支持多达32个节点,并且有输出短路保护。该芯片的框图如图29.1.2所示:

     

     

     

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

    本章,我们通过该芯片连接STM32的串口2,实现两个开发板之间的485通信。本章将实

     

    现这样的功能:通过连接两个精英STM32开发板的RS485接口,然后由KEY0控制发送,当按下一个开发板的KEY0的时候,就发送5个数据给另外一个开发板,并在两个开发板上分别显示发送的值和接收到的值。

    本章,我们只需要配置好串口2,就可以实现正常的485通信了,串口2的配置和串口1基本类似,只是串口的时钟来自APB1,最大频率为36Mhz。

    3.软件代码的实现

    在此,我将对几个重要的函数展开详细说明,我觉得外设模块化进行开发跟函数模块化开发一样重要,一个函数最多只放几十行代码,同时前面要有函数说明。这样,你写的代码会方便以后的修改,同时也可以让别人快速知道你这个软件实现的是什么功能。

    void RS485_Init(u32 bound) ;
    
    void RS485_Receive_Data(u8 *ReceiveBuf,u8 *len);
    
    void RS485_Send_Data(u8 *SendBuf ,u8 len);
    
     
    
        void RS485_Init(u32 bound)
    
    {
    
        GPIO_InitTypeDef GPIO_InitType;
    
        USART_InitTypeDef USART_InitType;
    
        NVIC_InitTypeDef NVIC_InitType;
    
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD,ENABLE);       //使能GPIOA和GPIOD的时钟
    
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);                       //使能USART2的时钟
    
     
    
       
    
        GPIO_InitType.GPIO_Pin=GPIO_Pin_7;                                   //接收或者发送模式控制引脚PD7(初始化之后才能用)
    
        GPIO_InitType.GPIO_Mode = GPIO_Mode_Out_PP;                       //推挽输出
    
        GPIO_InitType.GPIO_Speed=GPIO_Speed_50MHz;                     //这个引脚是485芯片中的发送接收控制引脚
    
        GPIO_Init(GPIOD,&GPIO_InitType);                                   //当PDout(7)输出的是高电平‘1’时是发送,为‘0’时是接收
    
       
    
        GPIO_InitType.GPIO_Pin=GPIO_Pin_2;                                  //USART2的TX引脚是PA2
    
        GPIO_InitType.GPIO_Mode=GPIO_Mode_AF_PP;
    
        GPIO_InitType.GPIO_Speed=GPIO_Speed_50MHz;
    
        GPIO_Init(GPIOA,&GPIO_InitType);
    
       
    
        GPIO_InitType.GPIO_Pin=GPIO_Pin_3;                                  //USART2的RX引脚是PA3
    
        GPIO_InitType.GPIO_Mode=GPIO_Mode_IN_FLOATING;
    
        GPIO_InitType.GPIO_Speed=GPIO_Speed_50MHz;
    
        GPIO_Init(GPIOA,&GPIO_InitType);
    
       
    
        #ifdef EN_RS485_RX
    
            USART_InitType.USART_BaudRate=bound;                                        //串口2的初始化
    
            USART_InitType.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
    
            USART_InitType.USART_Mode=USART_Mode_Rx|USART_Mode_Tx;
    
            USART_InitType.USART_Parity=USART_Parity_No;                       //其中重要的是USART_Mode要既能接收又能发送
    
            USART_InitType.USART_StopBits=USART_StopBits_1;                   //USART2的波特率都设置为9600
    
            USART_InitType.USART_WordLength=USART_WordLength_8b;
    
            USART_Init(USART2,&USART_InitType);
    
           
    
            NVIC_InitType.NVIC_IRQChannel=USART2_IRQn;                          //USART2中断初始化
    
            NVIC_InitType.NVIC_IRQChannelCmd=ENABLE;                            //其实这个中断主要是对发送的数据及时处理,使接收时便捷高效
    
            NVIC_InitType.NVIC_IRQChannelPreemptionPriority=3;
    
            NVIC_InitType.NVIC_IRQChannelSubPriority=3;
    
            NVIC_Init(&NVIC_InitType);
    
           
    
           
    
            USART_ITConfig(USART2,USART_IT_RXNE,ENABLE);            //要用到USART2中断除了初始化之外当然要对中断进行配置以及使能
    
            USART_Cmd(USART2,ENABLE);                                 //明确发生什么时会进入串口中断
    
        #endif
    
        RX485_TX_EN=0;              //默认接收模式   
    
    }
    
     
    
    这里的串口中断函数,发送的数据被接收数据寄存器接收到,就会触发中断,进行处理
    
    #ifdef EN_RS485_RX
    
     
    
    u8 RS485_RX_BUF[64];
    
    u8 RS485_RX_CNT=0;
    
    void USART2_IRQHandler(void)       //发送完数据就使能接收中断,使串口可以接收数据到缓冲区(数组RS485_RX_BUF)中
    
    {
    
        u8 Res;
    
        if(USART_GetITStatus(USART2,USART_IT_RXNE)!=RESET)     
    
        {
    
           
    
            Res=USART_ReceiveData(USART2);
    
            if(RS485_RX_CNT<64)                         //一次接收的数据最多为64,超过之后将不会对发送过来的数据进行缓存
    
            {  
    
                RS485_RX_BUF[RS485_RX_CNT]=Res;
    
                RS485_RX_CNT++;
    
            }  
    
        }
    
    }
    
     
    
    #endif
    
     
    
     
    
     
    
     
    
     
    
    void RS485_Send_Data(u8 *SendBuf ,u8 len)                       //发送处理函数
    
    {
    
        u8 i;
    
        RX485_TX_EN=1;
    
        for(i=0;i<len;i++)                                                          //把长度为len的数组SendBuf发送出去
    
        {
    
            while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET);        //等待发送移位数据寄存器的数据非空时(即发送结束)跳出来
    
            USART_SendData(USART2,SendBuf[i]);                                          //发送数据
    
        }
    
        while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET);
    
        RX485_TX_EN=0;                                                                  //默认接收模式   
    
        RS485_RX_CNT=0;
    
    }
    
     
    
     
    
     
    
    void RS485_Receive_Data(u8 *ReceiveBuf,u8 *len)                     //接收处理函数
    
    {
    
        u8 rxlen,i=0;
    
        rxlen=RS485_RX_CNT;
    
        delay_ms(10);
    
        if((rxlen==RS485_RX_CNT)&&rxlen)                             //把中断函数处理之后的缓存数据取出来
    
        {
    
            for(i=0;i<rxlen;i++)
    
            {
    
                ReceiveBuf[i]=RS485_RX_BUF[i];                       //传递接收到的数据以及长度回去
    
            }
    
            *len=RS485_RX_CNT;
    
            RS485_RX_CNT=0;                                            //对接收的个数进行清零,方便下次接收
    
        }
    
           
    
    }
  • 相关阅读:
    小程序滴滴车主板块的银行卡管理左滑删除编辑
    超好用超短的小程序请求封装
    如何使用css来让图片居中不变形 微信小程序和web端适用
    纯css手写圆角气泡对话框 微信小程序和web都适用
    小程序getUserInfo授权升级更新登录优化
    一起聊聊3个线程依次打印1、2、3...的故事
    influxdb基础那些事儿
    influxdb的命令们
    Linux namespace浅析
    kubernetes基础概念知多少
  • 原文地址:https://www.cnblogs.com/18689400042qaz/p/13336835.html
Copyright © 2020-2023  润新知