本程序开始之前已经开通了DMA的ADC通道
1、建立工程后,依次点击如图1、2、3、4、5
2、波特率选择9600,也可以选别的,能满足需求的波特率尽可能小,越小越稳定
3.1、开启DMA,添加Tx发送信息
3.2、 同样的方法,添加Rx接收信息
4、选择DMA优先级,如图,优先级数字越小,优先级程度越高
5.1、由于使用485硬件发送与接收,因此使用一个IO控制硬件的收发功能,高电平时为发送,低电平是为接收模式,所有只有发送的时候是高电平,其余皆为低电平,我选择的IO是PTA11
5.1.2、依次单机1、2
5.1.2、依次单机如图所示,由于硬件隔离电路问题选择了开漏模式,如果没有隔离可以选择推挽模式加上拉电阻,速度选择高速模式
5.2、完成后代码生成,并打开代码
6、在“main.h”中添加代码,位置如图
6.1、找到宏定义
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
/* USER CODE END EM */
添加代码 宏定义 接收到的数据长度BUFFER_SIZE 小于 100
/* Exported macro ------------------------------------------------------------*/
/* USER CODE BEGIN EM */
#define BUFFER_SIZE 100
/* USER CODE END EM */
6.2、找到代码
/* Private defines -----------------------------------------------------------*/
/* USER CODE BEGIN Private defines */
/* USER CODE END Private defines */
添加全局变量,extern是全局变量定义,就是定义的数组整个工程都可以用
/* Private defines -----------------------------------------------------------*/
/* USER CODE BEGIN Private defines */
extern unsigned int timen;//系统运行绝对时钟,从系统运行开始计时,每1ms自加一次
extern unsigned short ADC[10];
extern unsigned char RxBuff[BUFFER_SIZE];
extern unsigned char TxBuff[BUFFER_SIZE];
extern unsigned char TxEndFlag;//完成发送标志位
extern unsigned char RxEndFlag;//完成接收标志位
extern unsigned char RxLen;//接收数据长度
/* USER CODE END Private defines */
7、在“main.c”函数中添加代码如下,定义RxBuff是接收到暂存数组,每次接收到数据后都会把数据存放在这个数组中,因此有很强的实时性,TxBuff是发送暂存数组,每次发送都是这个数组的数据
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
unsigned int timen;//系统运行绝对时钟,从系统运行开始计时,每1ms自加一次
unsigned short ADC[10];
unsigned char RxBuff[BUFFER_SIZE];
unsigned char TxBuff[BUFFER_SIZE];
unsigned char TxEndFlag;//完成发送标志位
unsigned char RxEndFlag;//完成接收标志位
unsigned char RxLen;//接收数据长度
/* USER CODE END PV */
8、在“usart.c”文件下添加代码
8.1、找到一下代码
/* Includes ------------------------------------------------------------------*/
#include "usart.h"
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
添加 “#include "main.h" ” 如下,为了把定义的变量添加进去
/* Includes ------------------------------------------------------------------*/
#include "usart.h"
/* USER CODE BEGIN 0 */
#include "main.h"
/* USER CODE END 0 */
8.2.1、在“void MX_USART1_UART_Init(void)”函数的开头找到以下代码
/* USER CODE BEGIN USART1_Init 0 */
/* USER CODE END USART1_Init 0 */
添加 代码 “unsigned char RxBuff[BUFFER_SIZE]; ”
/* USER CODE BEGIN USART1_Init 0 */
unsigned char RxBuff[BUFFER_SIZE];
/* USER CODE END USART1_Init 0 */
8.2.2、在“void MX_USART1_UART_Init(void)”函数的末尾找到以下代码
/* USER CODE BEGIN USART1_Init 2 */
/* USER CODE END USART1_Init 2 */
添加 代码 “__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
HAL_UART_Receive_DMA(&huart1,RxBuff,BUFFER_SIZE);”
/* USER CODE BEGIN USART1_Init 2 */
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE); //使能IDLE中断
//DMA接收函数,此句一定要加,不加接收不到第一次传进来的实数据,是空的,且此时接收到的数据长度为缓存器的数据长度
HAL_UART_Receive_DMA(&huart1,RxBuff,BUFFER_SIZE);
/* USER CODE END USART1_Init 2 */
8.3、在“usart.c”文件的最后末尾添加代码,如图
/* USER CODE BEGIN 1 */
void PMJD_UART1_DMA_Send(uint8_t *buf,uint8_t len)
{
if(TxEndFlag==0)//发送
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_SET);
for(unsigned short i=0;i<280;i++);//280
if(HAL_UART_Transmit_DMA(&huart1, buf,len)!= HAL_OK) //判断是否发送正常,如果出现异常则进入异常中断函数
{
Error_Handler();
}
}
}
/*
*********************************************************************************************************
* 函 数 名: PMJD_UART1_DMA_Receive
* 功能说明: 串口接收功能函数
* 形 参: Data,len
* 返 回 值: 无
*********************************************************************************************************
*/
void PMJD_UART1_DMA_Receive(void)//串口接收封装uint8_t *Data,uint8_t len
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_11, GPIO_PIN_RESET);//BUFFER_SIZE
HAL_UART_Receive_DMA(&huart1,RxBuff,BUFFER_SIZE);//重新打开DMA接收
}
void Bsp_break_usartRx(void)
{
uint32_t tmp_flag = 0;
uint32_t temp;
tmp_flag =__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); //获取IDLE标志位
if((tmp_flag != RESET)&(TxEndFlag == 0))//idle标志被置位
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
HAL_UART_DMAStop(&huart1); //
temp = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);// 获取DMA中未传输的数据个数
RxLen = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,得到已经接收的数据个数
RxEndFlag = 1; // 接受完成标志位置1
}
}
void Bsp_break_usart_DMA_Tx(void)
{
TxEndFlag++;//为1时,初始进入中断,开始发送,为2,发送结束
if(TxEndFlag >= 2)
{
TxEndFlag = 0;
PMJD_UART1_DMA_Receive();//接收RS485
}
}
/* USER CODE END 1 */
9、打开“usart.h”文件,在文件末尾找到,如下
void MX_USART1_UART_Init(void);
/* USER CODE BEGIN Prototypes */
/* USER CODE END Prototypes */
添加代码
void MX_USART1_UART_Init(void);
/* USER CODE BEGIN Prototypes */
void PMJD_UART1_DMA_Send(uint8_t *buf,uint8_t len);//DMA发送数据
void PMJD_UART1_DMA_Receive(void);//DMA重启数据接收
void Bsp_break_usartRx(void);//用于接收中断
void Bsp_break_usart_DMA_Tx(void);//用于发送中断
/* USER CODE END Prototypes */
10、打开“stm32f1xx_it.c”文件
10.1、添加头文件 “ #include "usart.h" ”如下
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "usart.h"
/* USER CODE END Includes */
10.2、添加代码“ timen++; ”,“SysTick_Handler”函数是系统时钟,每1ms被调用一次
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
timen++;
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
10.3、添加代码 “ Bsp_break_usart_DMA_Tx(); ”,如图
void DMA1_Channel4_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel4_IRQn 0 */
/* USER CODE END DMA1_Channel4_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart1_tx);
/* USER CODE BEGIN DMA1_Channel4_IRQn 1 */
Bsp_break_usart_DMA_Tx();
/* USER CODE END DMA1_Channel4_IRQn 1 */
}
10.4、添加代码 “ Bsp_break_usartRx(); ”,如图
/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
Bsp_break_usartRx();
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
11、回到主函数“main.c”中,测试下,可不可以运行
在int main(void)函数中,添加代码 “ unsigned int time; ”如图
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
unsigned int time;//记录时钟,用于延时等
unsigned char TxBuffTest[BUFFER_SIZE];//用于测试发送数据时的储存器
/* USER CODE END 1 */
在" while(1) "下添加代码如下,每1s发送一次数据
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
for(unsigned char i=0;i<10;i++)
TxBuff[i]=0x00+i;//数据赋值
TxBuff[10]=0x0D;//这个和下面的共同表示发送结束
TxBuff[11]=0x0A;
if((TxEndFlag == 0)&(timen - time >= 1000))
{
time = timen;
PMJD_UART1_DMA_Send(TxBuff,12);//发送数据
}
}
/* USER CODE END 3 */
12、编译,烧入程序,测试接收数据,接收正常,如图,16进制,波特率 9600
13、测试下,接收数据再转发
在 “ while(1) ”的函数中添加代码,如下图
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
for(unsigned char i=0;i<10;i++)
TxBuff[i]=0x00+i;//数据赋值
TxBuff[10]=0x0D;//这个和下面的共同表示发送结束
TxBuff[11]=0x0A;
if((TxEndFlag == 0)&(timen - time >= 1000))
{
time = timen;
PMJD_UART1_DMA_Send(TxBuff,12);//发送数据
}
if(RxEndFlag == 1) //接收完成标志
{
for(unsigned char i=0;i<RxLen;i++)
{
TxBuffTest[i]=RxBuff[i];
}
PMJD_UART1_DMA_Send(TxBuff, RxLen);//发送接收到的数据
memset(RxBuff,0,RxLen);//清除接收到的数据
RxLen = 0;//清除计数
RxEndFlag = 0;//清除接收结束标志位
}
}
/* USER CODE END 3 */
由于使用了“memset”函数,需要调用标准函数库
在main.h文件中添加标准库的头文件 “ #include "string.h" ”
/* Includes ------------------------------------------------------------------*/
#include "stm32f1xx_hal.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "string.h"
/* USER CODE END Includes */
14、编译、烧入程序,测试收发性能,正常
注:用DMA发送数据,每个消息的数据地址不要用一样的,因为DMA发送的时候,CPU也在工作,DMA数据地址的数据要避免被CPU修改