百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术文章 > 正文

学习STM32编程——基于 RS-485 总线的多机通信应用开发

itomcoil 2025-06-15 16:58 8 浏览

关键代码:

usart2.c

/**
* @brief USART2 初始化
* @param baud:  波特率设置
* @retval None
*/
void USART2_Init(uint32_t baud)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
    
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA|RCC_AHB1Periph_GPIOG,ENABLE);
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); // 使能 USART2 时钟
    
    /* USART2 引脚复用映射 */
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2); //PA2 复用为 USART2
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource3, GPIO_AF_USART2); //PA3 复用为 USART2
    
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; //PA2 与 PA3
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;  // 复用功能
    GPIO_InitStructure.GPIO_Speed = GPIO_Fast_Speed;  // 速度 50MHz
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 推挽复用输出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  // 上拉
    GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置生效
    
    //PG8 推挽输出 , 用于 RS-485 模式控制
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //PG8
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; // 输出
    GPIO_InitStructure.GPIO_Speed = GPIO_High_Speed;  // 速度 100MHz
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; // 推挽输出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;  // 上拉
    GPIO_Init(GPIOG, &GPIO_InitStructure); // 配置生效
    
    /* USART2  初始化设置 */
    USART_InitStructure.USART_BaudRate = baud; // 波特率设置
    USART_InitStructure.USART_WordLength = USART_WordLength_8b; // 字长 8bit
    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); // 使能 USART2
    
    USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); // 开启接收中断
    USART_ITConfig(USART2, USART_IT_IDLE, ENABLE); // 开启空闲中断
    
    /* USART2 NVIC  配置 */
    NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
    RS485_RX_MODE; // 默认为接收模式
}
/**
* @brief USART2 发送一个字节
* @param ch:  要发送的字节数据
* @retval None
*/
void USART2_SendByte(uint8_t ch)
{
    /*  发送一个字节数据到 USART2 */
    USART_SendData(USART2, ch);
    /*  等待发送完毕 */
    while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
}
/**
* @brief USART2 发送一个字符串
* @param *str:  要发送的字符串
* @param strlen:  字符串长度
* @retval None
*/
void USART2_SendString(uint8_t *str, uint8_t strlen)
{
    unsigned int k = 0;
    RS485_TX_MODE; // 进入发送模式
    do
    {
        USART2_SendByte(*(str + k));
    } while (k++ < strlen);
    RS485_RX_MODE; // 进入接收模式
}

/**
* @brief USART2 中断服务函数
* @param None
* @retval None
*/
void USART2_IRQHandler(void)
{
    uint8_t Res, forclear;
    if (USART_GetITStatus(USART2, USART_IT_RXNE) != RESET)
    {
        Res = USART_ReceiveData(USART2);
        USART2_RX_Buffer[USART2_RX_Index++] = Res;
        /*  防止接收缓存下标溢出 */
        if (USART2_RX_Index >= USART2_RX_MAX)
            USART2_RX_Index = 0;
    }
    if (USART_GetITStatus(USART2, USART_IT_IDLE) != RESET)
    {
        USART2_RX_OverFlag = 1;
        forclear = USART_ReceiveData(USART2);
    }
}

main.c

int main(void)
{
    delay_init(168); // 延时函数初始化
    LED_Init(); //LED 端口初始化
    Key_Init(); // 按键端口初始化
    EXTIx_Init();  // 外部中断初始化
    USART1_Init(115200);  //USART1 初始化
    USART2_Init(115200);  //USART2 初始化
    DHT11_Init();  //DHT11 初始化
    SPI2_Init(); //SPI2 外设初始化
    OLED_Init(); //OLED 显示模块初始化
#ifdef SLAVE_DEV
    while (DHT11_Init())  // 等待 DHT11 初始化完成
    {
        printf("DHT11 Init Error!\r\n");
        delay_ms(500);
    }
    printf("DHT11 Init Success!\r\n");
#endif
    while (1)
    {
        count++;
#ifdef SLAVE_DEV
        if (data_upload_flag == 1) // 上报标志位为 1
        {
            if (count >= 200)
            {
                count = 0;
                /*  读取 DHT11 的温湿度值 */
                DHT11_Read_Data(&temperature, &humidity);
                /*  组合需要显示的信息 */
                data_feedback[0] = temperature;
                data_feedback[1] = humidity;
                buildFeedbackFrame(data_feedback, 2, 0x01, full_data_feedback);
                USART2_SendString(full_data_feedback,strlen((const char *)full_data_feedback));
            }
        }
#endif
#ifdef MASTER_DEV
        if (keyValue == KEY_D_PRESS) // 下键按下
        {
            keyValue = 0;
            arrayLength = \
            sizeof(cmd_upload_th)/sizeof(cmd_upload_th[0]);
            USART2_SendString(cmd_upload_th, arrayLength);
        }
        else if (keyValue == KEY_U_PRESS) // 上键按下
        {
            keyValue = 0;
            arrayLength = \
            sizeof(cmd_stop_upload_th) / sizeof(cmd_stop_upload_th[0]);
            USART2_SendString(cmd_stop_upload_th, arrayLength);
        }
        else if (keyValue == KEY_L_PRESS)// 左键按下
        {
            keyValue = 0;
            arrayLength = \
            sizeof(cmd_toggle_led) / sizeof(cmd_toggle_led[0]);
            USART2_SendString(cmd_toggle_led, arrayLength);
        }
#endif
        /*  一帧数据接收完毕 */
        if (USART2_RX_OverFlag == 1)
        {
            USART2_RX_OverFlag = 0;
            USART2_RX_Index = 0;
            cmd_rs485 = CMD_None;
            if (isCheckSumOK(USART2_RX_Buffer) == true)
            {
                /*  先清空数据帧结构体数据域 */
                memset(dataFrame_rs485.data, 0, 8);
                /*  解析源数据 */
                analysisDataFrame(USART2_RX_Buffer);
                /*  判断命令类型 */
                cmd_rs485 = getCommandType(&dataFrame_rs485);
            }
#ifdef MASTER_DEV
            /*  上报温湿度数据命令 */
            if (cmd_rs485 == CMD_UPLOAD_TH)
            {
                /* OLED 显示温湿度 */
                temperature = dataFrame_rs485.data[0];
                humidity = dataFrame_rs485.data[1];
                sprintf(tempString, "Temp:%d", temperature);
                sprintf(humiString, "Humi:%d", humidity);
                Show_TempHumiLight(); //OLED 显示温湿度数据
            }
            /*  翻转 LED 命令 */
            else if (cmd_rs485 == CMD_TOGGLE_LED)
            {
                /* OLED 显示 LED 状态 |  亮: 2 ,灭: 3 */
                printf("led state\r\n");
                if (dataFrame_rs485.data[0] == 3)
                {
                    sprintf(ledString, "LED:OFF");
                }
                else if (dataFrame_rs485.data[0] == 2)
                {
                    sprintf(ledString, "LED: ON");
                }
                OLED_Display_String(20, 48, ledString, 16);
            }
#endif
#ifdef SLAVE_DEV
            /*  上报温湿度数据命令 */
            if (cmd_rs485 == CMD_UPLOAD_TH)
            {
                data_upload_flag = 1;
            }
            /*  停止上报温湿度数据命令 */
            else if (cmd_rs485 == CMD_STOP_UPLOAD_TH)
            {
                data_upload_flag = 0;
            }
            /*  翻转 LED 命令 */
            else if (cmd_rs485 == CMD_TOGGLE_LED)
            {
                LED0 = ~LED0;
                /* LED 状态 |  亮: 2 ,灭: 3 */
                data_feedback[0] = GPIO_ReadInputDataBit(GPIOF, \
                GPIO_Pin_9) + 2;
                /*  反馈 LED 状态至通信主机 */
                buildFeedbackFrame(data_feedback, 1, 0x03, \
                full_data_feedback);
                USART2_SendString(full_data_feedback, \
                strlen((char *)full_data_feedback));
            }
#endif
            /*  用完清空 USART2 接收缓存 */
            memset(USART2_RX_Buffer, 0, 255);
        }
        if (count % 50 == 0)
            LED1 = ~LED1;
        delay_ms(10);
    }
}

/**
* @brief 获取收到的命令类型
* @param *dataFrame :数据帧结构体首地址
* @retval Command_EnumDef :命令的枚举类型
*/
Command_EnumDef getCommandType(DataFrame_TypeDef *dataFrame)
{
    switch (dataFrame->cmd) {
        case 1:// 上报温湿度数据
            return CMD_UPLOAD_TH;
        case 2:// 停止上报温湿度数据
            return CMD_STOP_UPLOAD_TH;
        case 3:// 翻转 LED
            return CMD_TOGGLE_LED;
        default:
            break;
    }
    return CMD_None;
}
/**
* @brief 检测数据帧校验和是否正确
* @param *rcvbuf : USART2 接收的数据缓存
* @retval bool : true 校验和正确 | false 校验和错误
*/
bool isCheckSumOK(uint8_t *rcvbuf)
{
    uint8_t checkSum = 0, index = 0, tempLength = 0;
    tempLength = rcvbuf[3];// 取出帧数据长度
    /*  判断帧起始,帧结束,校验和是否正确 */
    if(((rcvbuf[0]==0x55)&&(rcvbuf[5+tempLength]==0x0B))||((rcvbuf[0]==0xAA)&&(rcvbuf[5+tempLength]==0x0C)))
    {
        checkSum=rcvbuf[1]+rcvbuf[2]+rcvbuf[3];
        while(tempLength--)
        {
            checkSum +=*(rcvbuf+4+index);
            index++;
        }
        checkSum=checkSum%256;//保留低8位
    }
    if(checkSum==*(rcvbuf+4+index))
        return true;
    return false;
}
void analysisDataFrame(uint8_t *rcvbuf)
{
    uint8_t index = 0, dataLength = 0;    
    dataLength=rcvbuf[3];
    dataFrame_rs485.sof=rcvbuf[0];
    dataFrame_rs485.dstAddr=rcvbuf[1];
    dataFrame_rs485.cmd=rcvbuf[2];
    dataFrame_rs485.dataLen=rcvbuf[3];
    while(dataLength--)
    {
        dataFrame_rs485.data[index]=*(rcvbuf+4+index);
        index++;
    }
    dataFrame_rs485.checkSum=*(rcvbuf+4+index);
    dataFrame_rs485.eof=*(rcvbuf+5+index);
}

void buildFeedbackFrame(uint8_t *dataFb, uint8_t length, \
uint8_t cmd, uint8_t *full_dataFb)
{
    uint8_t index = 0, tempLength = 0;
    full_dataFb[0] = 0xAA;
    full_dataFb[1] = masterAddr;
    full_dataFb[2] = cmd;
    full_dataFb[3] = length;
    tempLength = length;
    full_dataFb[4 + length] = full_dataFb[1] + full_dataFb[2] + \
    full_dataFb[3];
    while (tempLength--)
    {
        full_dataFb[4 + index] = *(dataFb + index);
        full_dataFb[4 + length] += *(dataFb + index);  // 计算 checkSum 值
        index++;
    }
    full_dataFb[5 + length] = 0x0C;
}
/**
* @brief OLED 显示环境参数 ( 温度 / 湿度 )
* @param None
* @retval None
*/
void Show_TempHumiLight(void)
{
    OLED_Display_String(20, 0, "RS-485", 16);
    OLED_Display_String(20, 16, tempString, 16);
    OLED_Display_String(20, 32, humiString, 16);
}

相关推荐

Java 如何从一个 List 中随机获得元素

概述从一个List中随机获得一个元素是有关List的一个基本操作,但是这个操作又没有非常明显的实现。本页面主要向你展示如何有效的从List中获得一个随机的元素和可以使用的一些方法。选择一个...

想月薪过万吗?计算机安卓开发之&quot;集合&quot;

集合的总结:/***Collection*List(存取有序,有索引,可以重复)*ArrayList*底层是数组实现的,线程不安全,查找和修改快,增和删比较慢*LinkedList*底层是...

China Narrows AI Talent Gap With U.S. as Research Enters Engineering Phase: Report

ImagegeneratedbyAITMTPOST--ChinaisclosinginontheU.S.intheAIindustry-academia-research...

大促系统优化之应用启动速度优化实践

作者:京东零售宋维飞一、前言本文记录了在大促前针对SpringBoot应用启动速度过慢而采取的优化方案,主要介绍了如何定位启动速度慢的阻塞点,以及如何解决这些问题。希望可以帮助大家了解如何定位该类问...

MyEMS开源能源管理系统核心代码解读004

本期解读:计量表能耗数据规范化算法:myems/myems-normalization/meter.py代码见底部这段代码是一个用于计算和存储能源计量数据(如电表读数)的小时值的Python脚本。它主...

Java接口与抽象类:核心区别、使用场景与最佳实践

Java接口与抽象类:核心区别、使用场景与最佳实践一、核心特性对比1.语法定义接口:interface关键字定义,支持extends多继承接口javapublicinterfaceDrawabl...

超好看 vue2.x 音频播放器组件Vue-APlayer

上篇文章给大家分享了视频播放器组件vue-aliplayer,这次给大家推荐一款音频插件VueAplayer。vue-aplayer一个好看又好用的轻量级vue.js音乐播放器组件。清爽漂亮的U...

Linq 下的扩展方法太少了,MoreLinq 来啦

一:背景1.讲故事前几天看同事在用linq给内存中的两个model做左连接,用过的朋友都知道,你一定少不了一个叫做DefaultIfEmpty函数,这玩意吧,本来很流畅的from......

MapReduce过程详解及其性能优化(详细)

从JVM的角度看Map和ReduceMap阶段包括:第一读数据:从HDFS读取数据1、问题:读取数据产生多少个Mapper??Mapper数据过大的话,会产生大量的小文件,由于Mapper是基于虚拟...

手把手教你使用scrapy框架来爬取北京新发地价格行情(实战篇)

来源:Python爬虫与数据挖掘作者:霖hero前言关于Scrapy理论的知识,可以参考我的上一篇文章,这里不再赘述,直接上干货。实战演练爬取分析首先我们进入北京新发地价格行情网页并打开开发者工具,如...

屏蔽疯狂蜘蛛,防止CPU占用100%(mumu模拟器和雷电模拟器哪个更占用cpu)

站点总是某个时间段莫名的cpu100%,资源占用也不高,这就有必要怀疑爬虫问题。1.使用"robots.txt"规范在网站根目录新建空白文件,命名为"robots.txt&#...

Web黑客近年神作Gospider:一款基于Go语言开发的Web爬虫,要收藏

小白看黑客技术文章,一定要点首小歌放松心情哈,我最爱盆栽!开始装逼!Gospider是一款运行速度非常快的Web爬虫程序,对于爱好白帽黑客的小白来说,可谓是佳作!Gospider采用厉害的Go语言开发...

用宝塔面板免费防火墙屏蔽织梦扫描网站

今天教大家在免费的基础上屏蔽织梦扫描,首先您要安装宝塔面板,然后再安装免费的防火墙插件,我用的是Nginx免费防火墙,然后打开这个插件。设置GET-URL过滤设置一条简单的宝塔面板的正则规则就可以屏蔽...

蜘蛛人再捞4千万美元 连续三周蝉联北美票房冠军

7月15日讯老马追踪票房数据的北美院线联盟今天表示,“蜘蛛人:离家日”(Spider-Man:FarFromHome)击退两部新片的挑战,连续第2周勇夺北美票房冠军,海捞4530万美元。法新...

夏天到了,需要提防扁虱,真是又小又恐怖的动物

夏天马上要到了,你知道吗,扁虱是这个夏天最危险的动物之一,很少有动物能比它还凶猛。Whenitcomestosummer'slittledangers,fewarenastiert...