打印
[STM32F1]

STM32F103C8T6-RS485通讯完整版

[复制链接]
1018|7
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
单片机串口通讯有着许多通讯方式,例如R485/RS232/RS422/TTL/网口等等,在现实生活中RS485通讯是使用频率最高的一种通讯方式,因为485通讯是采用一对多的通讯方法,何为一对多,即一个主机若干个从机,一个负责发数据,其他负者数据的接收;那么那么多数据接收设备不会导致数据混乱吗?答案是肯定会的,在这时就需要用到通讯协议了。

通讯协议一般分为帧头、地址、数据包、帧尾组成;其中帧头一般是2byte,地址1byte,数据包看自己需要自定义字节大小,帧尾1byte;其中帧头是判断整体设备通讯接收数据的正确性,地址是用来区分接收设备的判断,帧尾一般是数据包的校验和,这样帧尾是随着数据的变化而变化,通讯失准确性更加精准。

使用特权

评论回复
沙发
个百zz分点个|  楼主 | 2024-1-31 20:36 | 只看该作者
以上为硬件MAX485通讯芯片连接定义,在本篇当中RXD、TXD我们使用C8T6当中的串口1进行配置(PA9、PA10),其中数据发送时需要把2、3脚拉高,接收数据时需置低;其中PA9需复用推挽输出,PA10浮空输入;

**  1脚和4脚(RO\DI)端分别为接收器的输出和驱动器的输入端

**2脚和3脚(RE/DE)端分别为接收和发送的使能端

** A端和B端分别为接收和发送的差分信号端。

使用特权

评论回复
板凳
个百zz分点个|  楼主 | 2024-1-31 20:36 | 只看该作者
MAX485芯片的结构和引脚都非常简单,内部含有一个驱动器和接收器。RO和DI端分别为接收器的输出和驱动器的输入端,与单片机连接时只需分别与单片机的RXD和TXD相连即可。RE和DE端分别为接收和发送的使能端,当/RE为逻辑0时,器件处于接收状态;当DE为逻辑1时,器件处于发送状态,因为MAX485工作在半双工状态,所以只需用单片机的一个管脚控制这两个引脚即可。其最主要工作原理就是将RS485信号转成单片机的TTL信号。

使用特权

评论回复
地板
个百zz分点个|  楼主 | 2024-1-31 20:37 | 只看该作者
以下为485通讯.c文件
#include "stm32f10x.h"                  // Device header
#include <stdio.h>
#include "Delay.h"
#include <stdarg.h>
#include <Serial.h>

uint8_t Serial_RxPacket[8];       //8个字节
uint8_t Serial_RxFlag;            //发送完成的标识符

void Serial_Init(void)
{
        GPIO_InitTypeDef GPIO_InitStructure;
        USART_InitTypeDef USART_InitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  //使能GPIOA
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE); //使能USART1时钟
       
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11;           //控制数据的接收、发送
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
       
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;      //复用推挽输出
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;            //485-TX
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
       
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;    //浮空输入
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;               //485-RX
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
       
        USART_InitStructure.USART_BaudRate = 9600;
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位
        USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位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);       //初始化USART1
       
        USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);  //接收中断使能       
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
       
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;    //分优先级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
        NVIC_Init(&NVIC_InitStructure);
        USART_Cmd(USART1, ENABLE);
        RS485_receive();   //默认打开接收机
}

void Serial_SendByte(uint8_t Byte)                        //发送单个字节
{
        RS485_send();                                           //开启发送模式,关闭接收模式
        USART_SendData(USART1, Byte);
        while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
        Delay_ms(2);
        RS485_receive();                                        //默认打开接收机
}

void Serial_SendArray(uint8_t *Array, uint16_t Length)    //发送数组式字节
{
        uint16_t i;
        for (i = 0; i < Length; i ++)
        {
                Serial_SendByte(Array[i]);
        }
}

uint8_t Serial_GetRxFlag(void)
{
        if (Serial_RxFlag == 1)
        {
                Serial_RxFlag = 0;      //开始标识
                return 1;
        }
        return 0;
}

uint8_t Sum_Check(uint8_t *buf, uint16_t len)
{
    uint16_t i = 0;
    uint8_t sum_temp = 0;

    for (i = 0; i < len; i++)
    {
        sum_temp += buf[i];
    }

    return sum_temp;
}

void USART1_IRQHandler(void)
{
        static uint8_t RxState = 0;
        static uint8_t pRxPacket = 0;
        if (USART_GetITStatus(USART1, USART_IT_RXNE) == SET)  //接收数据
        {
                uint8_t RxData = USART_ReceiveData(USART1);     //回传数据赋给RxData
                if (RxState == 0)
                {
                        if (RxData == 0xFF)      //帧头判断
                        {
                                RxState = 1;           //帧头无误标志位
                                pRxPacket = 0;
                        }
                }
                else if (RxState == 1)
                {
                        Serial_RxPacket[pRxPacket] = RxData;         //RxData分别赋给Serial_RxPacket[0]*Serial_RxPacket[3]
                        pRxPacket ++;
                        if (pRxPacket >= 6)
                        {
                                RxState = 2;           //数据接收完毕标志位
                        }
                }
                else if (RxState == 2)
                {
                        uint8_t sum;
                        sum = Sum_Check((u8*)Serial_RxPacket,6);
                        if (RxData == sum)                          //校验和验证
                        {
                                Serial_RxFlag = 1;                       //结束标识                          
                          Serial_SendArray(Serial_RxPacket,6);
                        }
                        if (RxData != sum) Serial_SendByte(0x00);  //发送校验不通过字节
                  pRxPacket = 0;  RxState = 0;               //pRxPacket、RxState重新赋值方便下次接收数据
                }       
                USART_ClearITPendingBit(USART1, USART_IT_RXNE);
        }
}

使用特权

评论回复
5
个百zz分点个|  楼主 | 2024-1-31 20:37 | 只看该作者
.h文件
#ifndef __SERIAL_H
#define __SERIAL_H
#include "stm32f10x.h"  

#define RS485_send()    GPIO_SetBits(GPIOA,GPIO_Pin_11);      //发送使能
#define RS485_receive() GPIO_ResetBits(GPIOA,GPIO_Pin_11);    //接收使能


extern uint8_t Serial_RxPacket[];    //extern 数组 方便数据调用

void Serial_Init(void);
void Serial_SendByte(uint8_t Byte);
void Serial_SendArray(uint8_t *Array, uint16_t Length);
void USART2_IRQHandler(void);

#endif

使用特权

评论回复
6
个百zz分点个|  楼主 | 2024-1-31 20:37 | 只看该作者
main函数
#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "OLED.h"                       //可以打印出来数据
#include "Serial.h"
#include "Key.h"                        //可以通过按键来发射数据

uint8_t KeyNum;

int main(void)
{
        OLED_Init();
        Key_Init();
        Serial_Init();                      //串口初始化
    Serial_SendByte(0XAA);              //上电初始化发送AA 表示设备通讯正常
        while (1)
        {
                KeyNum = Key_GetNum();
        }
}

使用特权

评论回复
7
个百zz分点个|  楼主 | 2024-1-31 20:37 | 只看该作者
以上为通讯全部代码,代码经过博主验证,通讯正常无异;

下图为数据测试截图,测试在100ms/次的发送频率下用通讯全部正常,无异常;

使用特权

评论回复
8
个百zz分点个|  楼主 | 2024-1-31 20:37 | 只看该作者
程序设计:

1.帧头正确校验码不对接收数据0x00;

2..帧头不对无回应;

3.帧头正确校验码正确回应接收数据;

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

50

主题

631

帖子

0

粉丝