1.采用485來進行數據通訊,串口采用的是DMA+空閑中斷來接收數據,但始終無法進入中斷!
2.采用串口通訊助手,發送數據可以正常接收到返回幀,證明器件沒有問題
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;
}
|