找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 954|回复: 7
收起左侧

采用RS485来进行数据通讯,串口采用的是DMA+空闲中断来接收数据,但始终无法进入中断

[复制链接]
ID:1081116 发表于 2023-5-31 15:25 | 显示全部楼层 |阅读模式
1.采用485来进行数据通讯,串口采用的是DMA+空闲中断来接收数据,但始终无法进入中断!

2.采用串口通讯助手,发送数据可以正常接收到返回帧,证明器件没有问题
51hei.png

rs485.h
    #ifndef __RS485_H_
    #define __RS485_H_
    #include "./SYSTEM/sys/sys.h"
    #include "./BSP/PCF8574/pcf8574.h"
    #include "./BSP/LED/led.h"
    #define DMA_REC_LEN 50
    /*IO申明*/
    #define USART_TX_PIN        GPIO_PIN_2
    #define USART_TX_PORT        GPIOA
    #define USART_RX_PIN        GPIO_PIN_3
    #define USART_RX_PORT        GPIOA
    /*IO引脚定义*/
    #define RS485_RE_IO 6
    /* 函数申明 */
    void MY_RS485_Init(uint32_t bound);
    void MY_RS485_DMA_Init(void);
    void RS485_Mode(uint8_t mode);
    void RS485_Send_Data(uint8_t *data,uint8_t len);
    void HAL_UART_ReceiveIdle(UART_HandleTypeDef *huart);
    void Copy_Data(uint8_t *buf,uint16_t len);
    #endif
4.rs485.c
    #include "./BSP/RS485/rs485.h"
    /* 采用的堵塞发送  DMA+空闲中断接收的模式来进行数据的收发 */
    #define UART2_DMA_RX            1                //1使能    0失能  使用DMA接收的原理
    #define UART2_DMA_TX            0                //1使能    0失能  使用DMA传输的原理   未写入
    #define CHECK_NONE_ONE_STOP        1                //1个停止位  1使能    0失能
    #define CHECK_NONE_TWO_STOP       0                //2个停止位
    #define CHECK_EVEN                0                //偶校验
    #define CHECK_ODD                0                //奇校验
    UART_HandleTypeDef USART2_RS485Handler;            //串口2的句柄
    DMA_HandleTypeDef  USART2_DMAHandler;            //DMA句柄
    uint8_t DMA_REC_BUF[DMA_REC_LEN]={0};            //最大不能超过50个字节  DMA接收缓存区
    uint16_t RX_CNT = 0;                            //计数值
    uint8_t rx_end_flag = 0;                        //接收完成标志位
    uint8_t Data_BackUp[DMA_REC_LEN]={0};            //数据备份接收缓存区
    uint16_t datalen_backup = 0;                    //当前数据接收缓存区中的数据量
    /*串口2空闲传输+DMA数据传输*/
    void MY_RS485_Init(uint32_t bound){
        /*申明IO口相关参数*/
        GPIO_InitTypeDef GPIO_InitStruct;
        __HAL_RCC_USART2_CLK_ENABLE();
        __HAL_RCC_GPIOA_CLK_ENABLE();  
    //    GPIO_InitStruct.Pin = USART_TX_PIN|USART_RX_PIN;               /* TX RX引脚 */
        GPIO_InitStruct.Pin = USART_TX_PIN;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;                          /* 复用推挽输出 */
    //    GPIO_InitStruct.Pull = GPIO_PULLUP;                          /* 上拉 */
        GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;                     /* 高速 */
        GPIO_InitStruct.Alternate = GPIO_AF7_USART2;                     /* 复用为USART2 */
        HAL_GPIO_Init(USART_TX_PORT, &GPIO_InitStruct);                  /* 初始化发送引脚 */
        GPIO_InitStruct.Pin  = USART_RX_PIN;
        GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
        GPIO_InitStruct.Pull = GPIO_NOPULL;
        HAL_GPIO_Init(USART_RX_PORT, &GPIO_InitStruct);
        /*配置RS485相关参数*/
        USART2_RS485Handler.Instance = USART2;
        USART2_RS485Handler.Init.BaudRate=bound;
        USART2_RS485Handler.Init.HwFlowCtl =UART_HWCONTROL_NONE;
        USART2_RS485Handler.Init.Mode =UART_MODE_TX_RX;
        USART2_RS485Handler.Init.Parity =UART_PARITY_NONE;
        USART2_RS485Handler.Init.WordLength =UART_WORDLENGTH_8B;
    #if(CHECK_NONE_ONE_STOP== 1)
        USART2_RS485Handler.Init.StopBits =UART_STOPBITS_1;
    #endif
    #if(CHECK_NONE_TWO_STOP== 1)
        USART2_RS485Handler.Init.StopBits =UART_STOPBITS_2;
    #endif
    #if(CHECK_EVEN== 1)
        USART2_RS485Handler.Init.Parity =UART_PARITY_EVEN;
    #endif
    #if(CHECK_ODD== 1)
        USART2_RS485Handler.Init.Parity =UART_PARITY_ODD;
    #endif
        HAL_UART_Init(&USART2_RS485Handler);
    //    __HAL_UART_ENABLE_IT(&USART2_RS485Handler,UART_IT_RXNE);
        USART2_RS485Handler.Instance->CR1 |= USART_CR1_RXNEIE;
        RS485_Mode(0);
    #if(UART2_DMA_RX== 1)
        /*连接DMA和USART_RX接受使能*/
    //    __HAL_UART_ENABLE_IT(&USART2_RS485Handler,UART_IT_IDLE);                //使能串口的空闲中断
        USART2_RS485Handler.Instance->CR1 |= USART_CR1_IDLEIE;
        HAL_UART_Receive_DMA(&USART2_RS485Handler, DMA_REC_BUF, DMA_REC_LEN);    //使能DMA接收,将数据存储在DMA_REC_BUF数据缓存区中    DMA_REC_LEN是定义的DMA的最大传输数量
    #endif
        /*开启接受中断&配置优先级*/
        HAL_NVIC_SetPriority(USART2_IRQn, 2, 1);
        HAL_NVIC_EnableIRQ(USART2_IRQn);
    }
    void MY_RS485_DMA_Init(void){
        /* 配置DMA接受句柄 */
        __HAL_RCC_DMA1_CLK_ENABLE();
        USART2_DMAHandler.Instance =DMA1_Stream5;
        USART2_DMAHandler.Init.Channel =DMA_CHANNEL_4;
        USART2_DMAHandler.Init.Direction =DMA_PERIPH_TO_MEMORY;
        USART2_DMAHandler.Init.PeriphInc =DMA_PINC_DISABLE;
        USART2_DMAHandler.Init.MemInc =DMA_MINC_ENABLE;
        USART2_DMAHandler.Init.PeriphDataAlignment =DMA_PDATAALIGN_BYTE;
        USART2_DMAHandler.Init.MemDataAlignment =DMA_MDATAALIGN_BYTE;
        USART2_DMAHandler.Init.Mode =DMA_CIRCULAR;
        USART2_DMAHandler.Init.Priority =DMA_PRIORITY_MEDIUM;
        USART2_DMAHandler.Init.FIFOMode =DMA_FIFOMODE_DISABLE;
        HAL_DMA_Init(&USART2_DMAHandler);
        __HAL_LINKDMA(&USART2_RS485Handler,hdmarx,USART2_DMAHandler);        //将USART2句柄与DMA句柄连接起来
        __HAL_DMA_ENABLE(&USART2_DMAHandler);
        /*  配置优先级 */
        HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 0, 0);
        HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
    }
    void  DMA1_Stream5_IRQHandler(void){
        HAL_DMA_IRQHandler(&USART2_DMAHandler);
    }
    void USART2_IRQHandler(void){
        LED1_TOGGLE();
        HAL_UART_IRQHandler(&USART2_RS485Handler);
        HAL_UART_ReceiveIdle(&USART2_RS485Handler);
        while(HAL_UART_Receive_DMA(&USART2_RS485Handler, DMA_REC_BUF, DMA_REC_LEN) != HAL_OK);
    }
    void HAL_UART_ReceiveIdle(UART_HandleTypeDef *huart){
    //    uint8_t res = 0;
    //    LED1_TOGGLE();
        if(huart->Instance == USART2){
    #if(UART2_DMA_RX== 1)
            if((__HAL_UART_GET_FLAG(&USART2_RS485Handler, UART_FLAG_IDLE)) != RESET){    //接受到了一帧数据
                __HAL_DMA_DISABLE(&USART2_DMAHandler);                                    //失能DMA传输
                RX_CNT = DMA_REC_LEN - __HAL_DMA_GET_COUNTER(&USART2_DMAHandler);        //定义的最大传输数量-通道中剩余的数量=接收到数量
                Copy_Data(DMA_REC_BUF,RX_CNT);
                __HAL_UART_CLEAR_FLAG(&USART2_RS485Handler, UART_FLAG_IDLE);            //清除相关标记位
                __HAL_DMA_ENABLE(&USART2_DMAHandler);                                    //使能DMA传输
                rx_end_flag = 1;
            }
    #endif
        }
    }
    //void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart){
    //    uint8_t res = 0, i;
    //   
    ////    LED1_TOGGLE();
    //    if(huart->Instance == USART2){
    //#if(UART2_DMA_RX== 0)
    //        if((__HAL_UART_GET_FLAG(&USART2_RS485Handler, UART_FLAG_IDLE)) != RESET){    //接受到了一帧数据
    //            __HAL_DMA_DISABLE(&USART2_DMAHandler);                                    //失能DMA传输
    //            RX_CNT = DMA_REC_LEN - __HAL_DMA_GET_COUNTER(&USART2_DMAHandler);        //定义的最大传输数量-通道中剩余的数量=接收到数量
    //            Copy_Data(DMA_REC_BUF,RX_CNT);
    //            __HAL_UART_CLEAR_FLAG(&USART2_RS485Handler, UART_FLAG_IDLE);            //清除相关标记位
    //            __HAL_DMA_ENABLE(&USART2_DMAHandler);                                    //使能DMA传输
    //            rx_end_flag = 1;
    //        }
    //#endif
    //    }
    //}
    //备份接收到的数据
    void Copy_Data(uint8_t *buf,uint16_t len){
        uint16_t i;
        datalen_backup = len;
        for(i = 0; i < len; i++){
            Data_BackUp = buf;
        }
    }
    void RS485_Send_Data(uint8_t *data,uint8_t len){
        RS485_Mode(1);
        while(__HAL_UART_GET_FLAG(&USART2_RS485Handler,UART_FLAG_TC) == RESET);            //等待传输完成
        HAL_UART_Transmit(&USART2_RS485Handler,data, len, 1000);                        //再次发送数据
        while(__HAL_UART_GET_FLAG(&USART2_RS485Handler,UART_FLAG_TC) == RESET);            //等待数据传输完成
        RX_CNT = 0;
        RS485_Mode(0);
    }
    /* 选择RS485的模式  1--w  0--r */
    void RS485_Mode(uint8_t mode){
        PCF8574_Write_Bit(RS485_RE_IO, mode);
    }
5.采用MODBUS来进行检测温度
    #ifndef __RS_WS_N018_H_
    #define __RS_WS_N018_H_
    #include "./SYSTEM/sys/sys.h"
    #include "./SYSTEM/delay/delay.h"
    #include "./SYSTEM/usart/usart.h"
    #include "./BSP/RS485/rs485.h"
    #include "string.h"
    /*设备地址*/
    #define DEV_ADDR 0x01
    /*功能码*/
    #define FUN_CODE 0x03
    /*寄存器地址*/
    #define HUMI_ADDR  0x0000
    #define TEMP_ADDR  0x0001
    /*函数申明*/
    uint16_t CRC_16_HEX(uint8_t *Buf, uint8_t CRC_CNT);
    void N018_Send_HTCommand(void);
    uint8_t N018_Scan(float *temp,float *humi);
    #endif
6.
    #include "./BSP/RS_WS_N018/rs_ws_n018.h"
    //extern
    extern uint8_t Data_BackUp[DMA_REC_LEN];
    extern uint8_t rx_end_flag;
    uint8_t RS485_Send_Buf[10] = {0};
    uint8_t CRC_BUF[2] ={0};
    //CRC16校验算法 适用于16进制处理
    uint16_t CRC_16_HEX(uint8_t *Buf, uint8_t CRC_CNT)
    {
        unsigned long CRC_Temp;
        unsigned char i,j;
        CRC_Temp = 0xffff;
        for (i=0; i<CRC_CNT; i++) {
    //        printf("%x\r\n",Buf);
            CRC_Temp ^= Buf;
            for (j=0; j<8; j++) {
                if (CRC_Temp & 0x01)
                    CRC_Temp = (CRC_Temp >>1 ) ^ 0xA001;
                else
                    CRC_Temp = CRC_Temp >> 1;
            }
        }
        return(CRC_Temp>>8|CRC_Temp<<8);  
    }
    /**
    *@brief send command of reading humidity and temperature
    */
    void N018_Send_HTCommand(void){
        uint8_t addr_buf[2] ={DEV_ADDR,FUN_CODE};
        /*发送读取温湿度指令*/
        RS485_Send_Buf[0] = addr_buf[0];
        RS485_Send_Buf[1] = addr_buf[1];
        RS485_Send_Buf[2] = (uint8_t)HUMI_ADDR>>8;                //发送湿度首地址
        RS485_Send_Buf[3] = (uint8_t)HUMI_ADDR&0xFF;
        RS485_Send_Buf[4] = 0x00;                                //发送两个字节长度,包括了温度和湿度
        RS485_Send_Buf[5] = 0x02;
        CRC_BUF[0] = (CRC_16_HEX(RS485_Send_Buf, 6)>>8);
        CRC_BUF[1] = CRC_16_HEX(RS485_Send_Buf, 6)&0xFF;
        RS485_Send_Buf[6] = CRC_BUF[0];
        RS485_Send_Buf[7] = CRC_BUF[1];
        RS485_Send_Data(RS485_Send_Buf,8);
    }
    uint8_t N018_Scan(float *temp,float *humi){
        uint8_t ret = 1;
        uint16_t temperature = 0, humidity = 0;
        /* 发送读取温湿度指令 */
    //    N018_Send_HTCommand();
        RS485_Send_Buf[0] = 0X01;
        RS485_Send_Buf[1] = 0X03;
        RS485_Send_Buf[2] = 0X00;                //发送湿度首地址
        RS485_Send_Buf[3] = 0X00;
        RS485_Send_Buf[4] = 0x00;                                //发送两个字节长度,包括了温度和湿度
        RS485_Send_Buf[5] = 0x02;
        RS485_Send_Buf[6] = 0XC4;
        RS485_Send_Buf[7] = 0X0B;
        RS485_Send_Data(RS485_Send_Buf,8);
        printf("send successfully\r\n");
         while(rx_end_flag == 0);                                            //接受到了湿度数据
        printf("receive successfully\r\n");
        if(Data_BackUp[0]==RS485_Send_Buf[0]&&Data_BackUp[1]==RS485_Send_Buf[1]){        //判断命令和地址是否一致
            CRC_BUF[0] = (CRC_16_HEX(Data_BackUp, 7)>>8);
            CRC_BUF[1] = CRC_16_HEX(Data_BackUp, 7)&0xFF;
            if(CRC_BUF[0]==Data_BackUp[7]&&CRC_BUF[1]==Data_BackUp[8]){                //判断校验码是否正确
                humidity = Data_BackUp[3]<<8|Data_BackUp[4];
                printf("humi:%d\r\n",humidity);//测试代码
                *humi = humidity % 10;
                if(Data_BackUp[5]&0x80)            //温度为负
                    temperature = ~(Data_BackUp[5]<<8|Data_BackUp[6])+1;
                else                        //温度为正
                    temperature = Data_BackUp[5]<<8|Data_BackUp[6];
                *temp = temperature % 10;
                ret = 0;
            }
        }
        printf("receive successfully\r\n");
        return ret;
    }

回复

使用道具 举报

ID:883242 发表于 2023-5-31 20:52 | 显示全部楼层
数据帧长度不固定没法用DMA来接收。
回复

使用道具 举报

ID:1081213 发表于 2023-5-31 21:08 | 显示全部楼层
数据帧长度不固定没法用DMA来接收。
回复

使用道具 举报

ID:1081116 发表于 2023-6-1 08:16 | 显示全部楼层
Hephaestus 发表于 2023-5-31 20:52
数据帧长度不固定没法用DMA来接收。

虽然数据帧长度不固定 但是设置了DMA最大接收长度呀
回复

使用道具 举报

ID:68189 发表于 2023-6-1 08:29 | 显示全部楼层
沈小小小仙女 发表于 2023-6-1 08:16
虽然数据帧长度不固定 但是设置了DMA最大接收长度呀

达不到设置的DMA数据长度,不会触发DMA中断。我是这么理解的。
回复

使用道具 举报

ID:1081116 发表于 2023-6-1 08:44 | 显示全部楼层
a1231233132 发表于 2023-5-31 21:08
数据帧长度不固定没法用DMA来接收。

DMA+空闲中断可以完成接收不定长的数据呀
回复

使用道具 举报

ID:744809 发表于 2023-6-1 10:59 | 显示全部楼层
应该是dma配置有问题,可以用stm32cubemx生成配置代码,更简单一些
回复

使用道具 举报

ID:1081116 发表于 2023-6-3 15:01 | 显示全部楼层
wpppmlah 发表于 2023-6-1 08:29
达不到设置的DMA数据长度,不会触发DMA中断。我是这么理解的。

好的 谢谢
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表