DMA介绍
DMA(Direct MemoryAccess,直接存储器访问)提供在外设与内存、存储器和存储器、外设与外设之间的高速数据传输使用。它允许不同速度的硬件装置来沟通,而不需要依赖于CPU,在这个时间中,CPU对于内存的工作来说就无法使用。
DMA的意义
-
数据搬运的工作比较耗时间。
-
数据搬运工作时效要求高(有数据来就要搬走) 。
-
没啥技术含量(CPU节约出来的时间可以处理更重要的事)。
-
DMA是数据搬运工,代替CPU搬运数据,为CPU节省资源让CPU做其他操作。
DMA搬运的数据
-
存储器:存储器包括自身的闪存(flash)或者内存(SRAM)以及外设的存储设备都可以作为访问的源或者目标。
-
外设:外设指的是spi、usart、iic、adc 等基于APB1、APB2或AHB时钟的外设。
DMA搬运的地点
-
存储器—>存储器(例如:复制某特别大的数据buf)
-
存储—>外设(例:将某数据buf写入串口TDR寄存器)
-
外设—>存储器(例如:将串口RDR寄存器写入某数据buf)
DMA通道
STM32F103C8T6有2个DMA控制器,DMA1有7个通道,DMA2有5个通道,一个通道每次只能搬运一个外设的数据,如果同时有多个外设的DMA请求,则按照通道优先级进行响应。
仲裁器
-
仲裁器会通过DMA通道请求的优先级来启动对外设/内存的访问,也就是哪个通道的优先级最高,DMA就先响应哪个通道。
-
DMA通道优先级管理由软件优先级和硬件优先级组成:
-
软件优先级:每个通道的优先权可以在DMA_CCRx寄存器中设置,有4个等级:
-
最高优先级(Very High)
-
高优先级(High)
-
中等优先级(Medium)
-
低优先级(Low)
-
硬件优先级:如果2个请求有相同的软件优先级,则较低编号的通道比较高编号的通道有较高的优先权。
控制器
DMA1
DMA2
DMA处理
在发生一个DMA请求事件后,外设向DMA控制器发送一个请求信号。DMA控制器根据通道优先级处理请求。当DMA控制器开始访问发出DMA请求的外设时,DMA控制器立即发送给外设一个应答信号。当外设从DMA控制器得到应答信号时,立即释放它的DMA请求。一旦外设释放了这个请求,DMA控制器同时撤销应答信号。如果有更多的请求时,外设可以启动下一个周期。
DMA传输的三个操作
-
从外设数据寄存器或者从当前外设/存储器地址寄存器指示的存储器地址取数据,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元。(从旧地点取数据)
-
存数据到外设数据寄存器或者当前外设/存储器地址寄存器指示的存储器地址,第一次传输时的开始地址是DMA_CPARx或DMA_CMARx寄存器指定的外设基地址或存储器单元。(到新地点存数据)
-
执行一次DMA_CNDTRx寄存器的递减操作,该寄存器包含未完成的操作数目。(操作数递减1次)
DMA传输的方式
正常模式(DMA Mode Normal)
-
一次DMA数据传输完后,停止DMA传送,也就是只传输一次。
-
要开始新的DMA传输,需要在关闭DMA通道的情况下,在DMA CNDTRx寄存器中重新写入传输数目。
循环传输模式(DMA Mode Circular)
-
当传输结束时,硬件自动会将传输数据量寄存器进行重装,进行下一轮的数据传输。 也就是多次传输模式。
-
主要用于处理循环缓冲区和连续的数据传输。
指针增量模式
外设和存储器指针在每次传输后可以自动向后递增或保持常量。当设置为增量模式时,下一个要传输的地址将是前一个地址加上增量值。
-
源指针和目标指针都设置为增量模式
-
源指针设置为增量模式
-
一般设计存储器的都开启指针增量模式
DMA中断
每个DMA通道都有3个事件(DMA半传输、DMA传输完成和DMA传输错误),这3个事件都可以成为一个单独的中断请求。
中断事件 |
事件标志位 |
使能控制位 |
DMA半传输 |
HTIF |
HTIE |
DMA传输完成 |
TCIF |
TCIE |
DMA传输错误 |
TEIF |
TEIE |
DMA实验1(存储器 -> 存储器)
使用DMA的方式将数组A的内容复制到数组B中,搬运完之后将数组B的内容通过串口打印到屏幕,同时每隔0.5s翻转一次LED1的电平。
STM32的hal库关于DMA的函数
HAL_DMA_Start()
开启某个DMA通道的数据运输。
原型:HAL_StatusTypeDef HAL_DMA_Start(DMA_HandleTypeDef _hdma, uint32_t SrcAddress, uint32_t DstAddress, uint32_t DataLength)
参数:
DMA_HandleTypeDef _hdma:DMA通道句柄
uint32_t SrcAddress:源数据地址
DstAddress:目标数据地址
DataLength:数据长度
实例:
#define BUF_SIZE 16
uint32_t srcBuf[BUF_SIZE] = {
0x00000000,0x11111111,0x22222222,0x33333333,
0x44444444,0x55555555,0x66666666,0x77777777,
0x88888888,0x99999999,0xaaaaaaaa,0xbbbbbbbb,
0xcccccccc,0xdddddddd,0xeeeeeeee,0xffffffff
};
uint32_t desBuf[BUF_SIZE];
HAL_DMA_Start(&hdma_memtomem_dma1_channel1,(uint32_t)srcBuf,(uint32_t)desBuf,sizeof(uint32_t) _ BUF_SIZE); //开启DMA1通道1的数据传输
HAL_DMA_GET_FLAG
检测某个DMA通道的数据传输情况。
原型:HAL_DMA_GET_FLAG(DMA_HandleTypeDef _hdma,__FLAG__)
参数:
DMA_HandleTypeDef *hdma:DMA通道句柄
__FLAG__:
DMA_FLAG_TCx:传输完成标志
DMA_FLAG_HTx:半传输完成标志
DMA_FLAG_TEx:传输错误标志
DMA_FLAG_GLx:全局中断标志
实例:HAL_DMA_GET_FLAG(&hdma_memtomem_dma1_channel1,DMA_FLAG_TC1) //检测DMA1通道1的数据传输是否完成,完成返回RESET
使用STM32CubeMX创建工程
配置SYS
配置RCC
配置GPIO
PB8配置成输出高电平
配置串口信息(UART1)
配置DMA
使用DMA1的通道1,传输方向为内存到内存
配置工程名称、工程路径
选择固件库
生成工程
使用MicroLIB库
main.c文件编写
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include <stdio.h>
#define BUF_SIZE 16
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//重写stdio.h文件中的prinft()里的fputc()函数
int fputc(int my_data,FILE *p)
{
unsigned char temp = my_data;
//改写后,使用printf()函数会将数据通过串口一发送出去
HAL_UART_Transmit(&huart1,&temp,1,0xffff); //0xfffff为最大超时时间
return my_data;
}
//源数组
uint32_t srcBuf[BUF_SIZE] = {
0x00000000,0x11111111,0x22222222,0x33333333,
0x44444444,0x55555555,0x66666666,0x77777777,
0x88888888,0x99999999,0xaaaaaaaa,0xbbbbbbbb,
0xcccccccc,0xdddddddd,0xeeeeeeee,0xffffffff
};
//目标数组
uint32_t desBuf[BUF_SIZE];
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
int i = 0;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
//①:开启DMA1通道1的数据传输
HAL_DMA_Start(&hdma_memtomem_dma1_channel1,(uint32_t)srcBuf,(uint32_t)desBuf,sizeof(uint32_t) * BUF_SIZE); //DMA1通道1的数据开始传输
//②:等待DMA1通道1数据传输完成
while(__HAL_DMA_GET_FLAG(&hdma_memtomem_dma1_channel1,DMA_FLAG_TC1) == RESET); //等待某个通道的数据传输完成,flag标志位置位RESET
//③:打印数组内容
for(i = 0;i < BUF_SIZE;i++){
printf("Buf[%d] = %X\r\n",i,desBuf[i]);
}
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
HAL_Delay(500);
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
DMA实验2(存储器 -> 外设:UART1)
使用DMA的方式将存储器的内容发送到串口1,同时每隔0.5s翻转一次LED1的电平。
STM32的hal库关于DMA的函数
HAL_UART_Transmit_DMA()
使用DMA将其他地方的数据搬运到串口。
原型:HAL_StatusTypeDef HAL_UART_Transmit_DMA(UART_HandleTypeDef _huart, uint8_t _pData, uint16_t Size)
参数:
UART_HandleTypeDef _huart:串口句柄
uint8_t _pData:传输数据地址
uint16_t Size:传输数据大小
实例:
#define BUF_SIZE 1000
unsigned char sendBuf[BUF_SIZE] = {0};
for(i = 0;i < BUF_SIZE;i++){
sendBuf[i] = ‘h’;
}
HAL_UART_Transmit_DMA(&huart1,sendBuf,BUF_SIZE); //将数据通过串口DMA发送
使用STM32CubeMX创建工程
配置SYS
配置RCC
配置GPIO
PB8配置成输出高电平
配置串口信息(UART1)
配置DMA
-
使用DMA1的通道4,传输方向为内存到外设(串口1)
-
配置成正常模式只会向串口发送一次数组内容,而配置成循环模式将不断向串口发送数组内容
配置工程名称、工程路径
选择固件库
生成工程
使用MicroLIB库
main.c文件编写
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#define BUF_SIZE 1000
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
//待发送数据
unsigned char sendBuf[BUF_SIZE] = {0};
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
int i = 0;
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
//①:准备数据
for(i = 0;i < BUF_SIZE;i++){
sendBuf[i] = 'h';
}
//②:将数据通过串口DMA发送
HAL_UART_Transmit_DMA(&huart1,sendBuf,BUF_SIZE); //将数据通过串口DMA发送
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
HAL_Delay(500);
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
DMA实验3(外设:UART1 -> 存储器)
使用DMA的方式将串口1接收缓存寄存器的数据搬运到存储器中,再通过串口打印存储器数据,同时每隔0.5s翻转一次LED1的电平。
实现流程
-
使能IDLE空闲中断,当检测到串口空闲时(串口每接收完一份数据)就会调用一次串口中断。
-
将串口数据使用DMA搬运到内存。
-
在串口的中断处理函数USART1_IRQHandler()中将内存的数据显示在串口中:
-
判断是否是串口空闲触发的中断,即判断IDLE标志位是否置位SET。
-
停止DMA从串口搬运数据到内存。
-
获取DMA从串口搬运的数据大小。
-
将DMA从串口搬运的内存数据打印在串口。
-
重新将串口数据使用DMA搬运到内存,准备下一次的重复操作。
STM32的hal库关于DMA的函数
HAL_UART_Receive_DMA()
使用DMA搬运串口数据到其他地方
原型:
HAL_StatusTypeDef HAL_UART_Receive_DMA(UART_HandleTypeDef _huart, uint8_t _pData, uint16_t Size)
参数:
UART_HandleTypeDef _huart:串口句柄
uint8_t *pData:传输数据地址
uint16_t Size:传输数据大小
实例:
#define BUF_SIZE 1000
uint8_t rcvBuf[BUF_SIZE] = {0};
HAL_UART_Receive_DMA(&huart1,rcvBuf,BUF_SIZE); //将串口数据通过DMA接收到内存
HAL_UART_ENABLE_IT()
使能某个串口标志位的中断。
原型:
HAL_UART_ENABLE_IT(__HANDLE__, __INTERRUPT__)
参数:
__HANDLE__:串口句柄
__INTERRUPT__:
UART_IT_CTS: CTS change interrupt
UART_IT_LBD: LIN Break detection interrupt
UART_IT_TXE: Transmit Data Register empty interrupt
UART_IT_TC: Transmission complete interrupt
UART_IT_RXNE: Receive Data register not empty interrupt
UART_IT_IDLE: 串口空闲中断
UART_IT_PE: Parity Error interrupt
UART_IT_ERR: Error interrupt(Frame error, noise error, overrun error)
实例:
HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE); //使能IDLE空闲中断
HAL_UART_GET_FLAG()
获取串口的某个标志位的状态
原型:
#define HAL_UART_GET_FLAG(HANDLE__, __FLAG__) (((__HANDLE__)->Instance->SR & (__FLAG__)) == (__FLAG__))
参数:
_HANDLE__:串口句柄
__FLAG__:
UART_FLAG_CTS: CTS Change flag (not available for UART4 and UART5)
UART_FLAG_LBD: LIN Break detection flag
UART_FLAG_TXE: Transmit data register empty flag
UART_FLAG_TC: Transmission Complete flag
UART_FLAG_RXNE: Receive data register not empty flag
UART_FLAG_IDLE: 串口空闲标志位
UART_FLAG_ORE: Overrun Error flag
UART_FLAG_NE: Noise Error flag
UART_FLAG_FE: Framing Error flag
UART_FLAG_PE: Parity Error flag
实例:
HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) //获取IDLE标志位的状态,如果被置位了为SET
HAL_UART_DMAStop()
停止DMA从串口搬运数据或者停止DMA搬运数据到串口。
原型:
HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef _huart)
参数:
UART_HandleTypeDef _huart:串口句柄
实例:
HAL_UART_DMAStop(&huart1); //停止DMA从串口搬运数据
使用STM32CubeMX创建工程
配置SYS
配置RCC
配置GPIO
PB8配置成输出高电平
配置串口信息(UART1)
配置DMA
使用DMA1的通道5,传输方向为外设(串口1)到内存
配置工程名称、工程路径
选择固件库
生成工程
使用MicroLIB库
main.c文件编写
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "dma.h"
#include "usart.h"
#include "gpio.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#define BUF_SIZE 100
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t rcvBuf[BUF_SIZE] = {0}; //接收数据缓存数组
uint8_t rcvLen = 0; //接收一份数据的长度
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_DMA_Init();
MX_USART1_UART_Init();
/* USER CODE BEGIN 2 */
//使能IDLE空闲中断,当检测到串口空闲时就会调用串口中断
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
//DMA搬运串口数据到内存
HAL_UART_Receive_DMA(&huart1,rcvBuf,BUF_SIZE);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
HAL_GPIO_TogglePin(GPIOB,GPIO_PIN_8);
HAL_Delay(500);
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
stm32f1xx_it.c文件编写
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file stm32f1xx_it.c
* @brief Interrupt Service Routines.
******************************************************************************
* @attention
*
* Copyright (c) 2023 STMicroelectronics.
* All rights reserved.
*
* This software is licensed under terms that can be found in the LICENSE file
* in the root directory of this software component.
* If no LICENSE file comes with this software, it is provided AS-IS.
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f1xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */
/* USER CODE END TD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
extern uint8_t rcvBuf[BUF_SIZE]; //接收数据缓存数组
extern uint8_t rcvLen; //接收一帧数据的长度
/* USER CODE END 0 */
/* External variables --------------------------------------------------------*/
extern DMA_HandleTypeDef hdma_usart1_rx;
extern DMA_HandleTypeDef hdma_usart1_tx;
extern UART_HandleTypeDef huart1;
/* USER CODE BEGIN EV */
/* USER CODE END EV */
/******************************************************************************/
/* Cortex-M3 Processor Interruption and Exception Handlers */
/******************************************************************************/
/**
* @brief This function handles Non maskable interrupt.
*/
void NMI_Handler(void)
{
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
/* USER CODE END NonMaskableInt_IRQn 0 */
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
while (1)
{
}
/* USER CODE END NonMaskableInt_IRQn 1 */
}
/**
* @brief This function handles Hard fault interrupt.
*/
void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
/* USER CODE END HardFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
/* USER CODE END W1_HardFault_IRQn 0 */
}
}
/**
* @brief This function handles Memory management fault.
*/
void MemManage_Handler(void)
{
/* USER CODE BEGIN MemoryManagement_IRQn 0 */
/* USER CODE END MemoryManagement_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
/* USER CODE END W1_MemoryManagement_IRQn 0 */
}
}
/**
* @brief This function handles Prefetch fault, memory access fault.
*/
void BusFault_Handler(void)
{
/* USER CODE BEGIN BusFault_IRQn 0 */
/* USER CODE END BusFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_BusFault_IRQn 0 */
/* USER CODE END W1_BusFault_IRQn 0 */
}
}
/**
* @brief This function handles Undefined instruction or illegal state.
*/
void UsageFault_Handler(void)
{
/* USER CODE BEGIN UsageFault_IRQn 0 */
/* USER CODE END UsageFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_UsageFault_IRQn 0 */
/* USER CODE END W1_UsageFault_IRQn 0 */
}
}
/**
* @brief This function handles System service call via SWI instruction.
*/
void SVC_Handler(void)
{
/* USER CODE BEGIN SVCall_IRQn 0 */
/* USER CODE END SVCall_IRQn 0 */
/* USER CODE BEGIN SVCall_IRQn 1 */
/* USER CODE END SVCall_IRQn 1 */
}
/**
* @brief This function handles Debug monitor.
*/
void DebugMon_Handler(void)
{
/* USER CODE BEGIN DebugMonitor_IRQn 0 */
/* USER CODE END DebugMonitor_IRQn 0 */
/* USER CODE BEGIN DebugMonitor_IRQn 1 */
/* USER CODE END DebugMonitor_IRQn 1 */
}
/**
* @brief This function handles Pendable request for system service.
*/
void PendSV_Handler(void)
{
/* USER CODE BEGIN PendSV_IRQn 0 */
/* USER CODE END PendSV_IRQn 0 */
/* USER CODE BEGIN PendSV_IRQn 1 */
/* USER CODE END PendSV_IRQn 1 */
}
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
/******************************************************************************/
/* STM32F1xx Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_stm32f1xx.s). */
/******************************************************************************/
/**
* @brief This function handles DMA1 channel4 global interrupt.
*/
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 */
/* USER CODE END DMA1_Channel4_IRQn 1 */
}
/**
* @brief This function handles DMA1 channel5 global interrupt.
*/
void DMA1_Channel5_IRQHandler(void)
{
/* USER CODE BEGIN DMA1_Channel5_IRQn 0 */
/* USER CODE END DMA1_Channel5_IRQn 0 */
HAL_DMA_IRQHandler(&hdma_usart1_rx);
/* USER CODE BEGIN DMA1_Channel5_IRQn 1 */
/* USER CODE END DMA1_Channel5_IRQn 1 */
}
/**
* @brief This function handles USART1 global interrupt.
*/
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
//如果是串口1空闲触发的中断,IDLE标志位会置位SET
if(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE) == SET){
//__HAL_UART_CLEAR_FEFLAG(&huart1); //清除帧错误标志位FE,该位会提醒当前字符是否存在帧错误
HAL_UART_DMAStop(&huart1); //停止DMA从串口1中搬运数据
uint8_t tempLen = __HAL_DMA_GET_COUNTER(&hdma_usart1_rx); //获取DMA未搬运的数据大小
rcvLen = BUF_SIZE - tempLen; //DMA当前搬运了的数据大小
HAL_UART_Transmit(&huart1,rcvBuf,rcvLen,0xffff); //将内存数据通过DNA发送到串口
HAL_UART_Receive_DMA(&huart1,rcvBuf,BUF_SIZE); //重新开始DMA搬运串口数据到内存
}
/* USER CODE END USART1_IRQn 1 */
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
评论(0)
您还未登录,请登录后发表或查看评论