1、打开 STM32CubeMX -> New Project -> MCU Selector -> STM32F101RBTx
2、选择 FREERTOS 系统
3、选择 RCC 高速时钟 PD0 和 PD1
4、选择 SYS TIM4 作为基础时钟 , SWD(PA13 和PA14 )
DEBUG 选择 Serial Wire 为St-link SWD 调试管脚,可以不选 ,上电默认就是调试管脚;
注意 :Timebase Source 为啥要选TIM4 而不是Systick,因为FREERTOS 要使用Systick 作为调度时钟
5、选择 USART1 异步串口 PA9 和PA10
6、选择 LED1 和 LED2 GPIO模式, PA0 和 PA1
7、时钟网络 clock configuration 配置 成最高36M时钟
8、配置 usart1
打开 usart 中断
添加 usart1 接收 和发送 DMA通道
9 、配置 FREERTOS
添加 3 个任务 LED1Task、LED2Task、Usart1Task,其他配置不变
10、点击生成代码,要求先设置工程
MDK 环境选择 keil 5
勾选 generate peripheral **** .c/.h **** 选项,保证 不同模块单独文件;
11 、打开工程
在 gpio.c 中添加 2函数 操作LED 灯
/* USER CODE BEGIN 2 */
void Led1_op(int val)
{
if (val)
{
HAL_GPIO_WritePin(GPIOA, LED1_Pin, GPIO_PIN_SET);
}
else
{
HAL_GPIO_WritePin(GPIOA, LED1_Pin, GPIO_PIN_RESET);
}
}
void Led2_op(int val)
{
if (val)
{
HAL_GPIO_WritePin(GPIOA, LED2_Pin, GPIO_PIN_SET);
}
else
{
HAL_GPIO_WritePin(GPIOA, LED2_Pin, GPIO_PIN_RESET);
}
}
/* USER CODE END 2 */
在 freertos.c 中 修改
/* StartLED1Task function */
void StartLED1Task(void const * argument)
{
/* USER CODE BEGIN StartLED1Task */
int flag = 0;
/* Infinite loop */
for(;;)
{
Led1_op(flag);
flag = !flag;
osDelay(500); // 500ms 交替
}
/* USER CODE END StartLED1Task */
}
/* StartLED2Task function */
void StartLED2Task(void const * argument)
{
/* USER CODE BEGIN StartLED2Task */
int flag = 0;
/* Infinite loop */
for(;;)
{
Led2_op(flag);
flag = !flag;
osDelay(1000); // 1000ms 交替
}
/* USER CODE END StartLED2Task */
}
接下来修改 串口dma 收发,在 usart.h 中添加 结构
/* USER CODE BEGIN Private defines */
#define RECEIVELEN 1024
#define USART_DMA_SENDING 1 //发送未完成
#define USART_DMA_SENDOVER 0 //发送完成
typedef struct
{
uint8_t receive_flag:1;//空闲接收标记
uint8_t dmaSend_flag:1;//发送完成标记
uint16_t rx_len;//接收长度
uint8_t usartDMA_rxBuf[RECEIVELEN];//DMA接收缓存
}USART_RECEIVETYPE;
extern USART_RECEIVETYPE UsartType1;
/* USER CODE END Private defines */
在 usart.c 中添加
/* USER CODE BEGIN 1 */
#ifdef __GNUC__
/* With GCC/RAISONANCE, small printf (option LD Linker->Libraries->Small printf
set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
HAL_UART_Transmit(&huart1 , (uint8_t *)&ch, 1, 0xFFFF);
return ch;
}
//DMA发送函数
void Usart1SendData_DMA(uint8_t *pdata, uint16_t Length)
{
while(UsartType1.dmaSend_flag == USART_DMA_SENDING);
UsartType1.dmaSend_flag = USART_DMA_SENDING;
HAL_UART_Transmit_DMA(&huart1, pdata, Length);
}
//DMA发送完成中断回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
__HAL_DMA_DISABLE(huart->hdmatx);
UsartType1.dmaSend_flag = USART_DMA_SENDOVER;
}
//串口接收空闲中断
void UsartReceive_IDLE(UART_HandleTypeDef *huart)
{
uint32_t temp;
if((__HAL_UART_GET_FLAG(huart,UART_FLAG_IDLE) != RESET))
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);
HAL_UART_DMAStop(&huart1);
temp = huart1.hdmarx->Instance->CNDTR;
UsartType1.rx_len = RECEIVELEN - temp;
UsartType1.receive_flag=1;
HAL_UART_Receive_DMA(&huart1,UsartType1.usartDMA_rxBuf,RECEIVELEN);
}
}
/* USER CODE END 1 */
在 dma.c 中注释掉
//HAL_NVIC_SetPriority(DMA1_Channel5_IRQn, 5, 0);
//HAL_NVIC_EnableIRQ(DMA1_Channel5_IRQn);
dma 接收中断关闭,不需要接收中断,本程序使用串口空闲中断读取dma数据;
在 mian.c ,mian 函数中添加
/* USER CODE BEGIN 2 */
HAL_UART_Receive_DMA(&huart1, UsartType1.usartDMA_rxBuf, RECEIVELEN);
__HAL_UART_ENABLE_IT(&huart1, UART_IT_IDLE);
/* USER CODE END 2 */
使能串口 dma接收和空闲中断
在 freertos.c 中,修改
/* StartUsart1Task function */
void StartUsart1Task(void const * argument)
{
/* USER CODE BEGIN StartUsart1Task */
/* Infinite loop */
for(;;)
{
if(UsartType1.receive_flag)//如果产生了空闲中断
{
UsartType1.receive_flag=0;//清零标记
Usart1SendData_DMA(UsartType1.usartDMA_rxBuf,UsartType1.rx_len);//串口打印收到的数据。
}
}
/* USER CODE END StartUsart1Task */
}
实现串口接收到的内容回送。
编译加载 ,运行 ok !