沈小小小仙女 2023-05-31 14:25 采纳率: 33.3%
浏览 27

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

1.采用485来进行数据通讯,串口采用的是DMA+空闲中断来接收数据,但始终无法进入中断!
2.采用串口通讯助手,发送数据可以正常接收到返回帧,证明器件没有问题

img

3.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[i] = buf[i];
    }
}

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[i]);
        CRC_Temp ^= Buf[i];
        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;
}

  • 写回答

2条回答 默认 最新

  • 于扶摇 2023-05-31 15:14
    关注

    中断被禁止:检查中断是否被禁止,如果被禁止,请确保在正确的位置启用中断。
    硬件连接问题:确保您的串口硬件连接正确,且没有任何松动或损坏。
    串口配置问题:检查您的串口配置是否正确。例如,波特率、数据位、停止位、校验位等设置是否正确。
    空闲中断程序问题:检查您的空闲中断程序是否正确编写。它应该能够正确地接收数据并在中断发生时执行相应的操作。

    评论

报告相同问题?

问题事件

  • 创建了问题 5月31日

悬赏问题

  • ¥15 x趋于0时tanx-sinx极限可以拆开算吗
  • ¥500 把面具戴到人脸上,请大家贡献智慧
  • ¥15 任意一个散点图自己下载其js脚本文件并做成独立的案例页面,不要作在线的,要离线状态。
  • ¥15 各位 帮我看看如何写代码,打出来的图形要和如下图呈现的一样,急
  • ¥30 c#打开word开启修订并实时显示批注
  • ¥15 如何解决ldsc的这条报错/index error
  • ¥15 VS2022+WDK驱动开发环境
  • ¥30 关于#java#的问题,请各位专家解答!
  • ¥30 vue+element根据数据循环生成多个table,如何实现最后一列 平均分合并
  • ¥20 pcf8563时钟芯片不启振