你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

【NUCLEO-U083RC评测】+ 工程模板(串口DMA+IDLE+printf重定向+软中断处理串口数据+非阻塞延时任务)

[复制链接]
Joseph_chn 发布时间:2024-5-28 09:14

前两天收到了ST社区的NUCLEO-U083RC的评估板,这颗芯片中内置了硬件AES与RNG模块,准备对这两个模块做个测试,今天先配置个工程模板,主要开启串口收发,printf重定向,利用DMA与IDLE中断自动接收不定长的串口数据,在接收完毕后利用软中断触发中断处理串口数据,在编写一个简易的非阻塞延时功能。

全部代码以上传到github:https://github.com/what-sudo/stm32U083

本贴同步发表到本人CSDN账号:文章地址:STM32Cube系列教程10:STM32CubeIDE工程创建+串口DMA+IDLE+printf重定向+软中断处理串口数据+非阻塞延时任务-CSDN博客

工程配置

配置时钟

打开STM32CubeIDE环境,创建一个新项目,选择对应的芯片,打开STM32CubeMX代码生成工具。 打开System Core -> RCC,分别配置外部高速时钟与外部低速时钟源为晶体振荡器

becb1c2fa83349fc98de0d9a43fa9ebe.png

打开System Core -> SYS,配置系统时基为SysTick。

6eee1c221fea47caa00a33cec8d8172f.png

配置Debug接口

打开Trace and Debug -> Debug配置选项,选中Serial Wire。打开SWD功能。这里如果不打开,后期将无法使用swd功能烧写程序。 01b12da2e71a4a95badd2173dc87b0d8.png

配置串口外设

打开Connectivity -> LPUART1配置选项,模式设置为异步串行接口,下面波特率设置为115200,检查右侧显示的串口管脚是否与硬件匹配,如果不匹配可重定向到别的管脚。

abbe02bf35644e8d90eee15e4348d89d.png 配置串口DMA通道 36ffa0805867499e88d2b18ef5280ab9.png 使能串口中断源 00c751f6c60e413c99eb1fdcd35de4ab.png

配置时钟树

为了获取更好的性能,这里选择使用外部时钟,注意设置外部晶体的频率,系统时钟源选择PLLCLK,HCLK时钟配置为48MHz。 939abafa57c64772a4235136bd51111f.png

生成代码

点击上方的Project manager,打开Code Generator,选中仅复制使用的库文件,生成单独的.c与.h文件。 b00f72d1eea9413db1ba529af16950f8.png 设置完成后,Ctrl + S保存,将自动弹窗提示生成代码,或手动点击上方的小齿轮图标生成代码 6711ee4fdb4f42b39dd05d747c154835.png

配置串口

重定向printf

打开Core\Src\usart.c文件,在文件前部加入重定向printf的代码。

#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#define GETCHAR_PROTOTYPE int __io_getchar(FILE *f)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#define GETCHAR_PROTOTYPE int fgetc(FILE *f)
#endif /* __GNUC__ */
PUTCHAR_PROTOTYPE
{
  HAL_UART_Transmit(&hlpuart1, (uint8_t *)&ch, 1, 0xFFFF);
  return ch;
}
GETCHAR_PROTOTYPE
{
  uint8_t ch = 0;
  HAL_UART_Receive(&hlpuart1, (uint8_t *)&ch, 1, 0xFFFF);
  if (ch == '\r')
  {
    __io_putchar('\r');
    ch = '\n';
  }
  return __io_putchar(ch);
}

916b9a259ebe4542abd5fa6b83ed962d.png

配置串口,开启IDLE,开启软中断

打开usart.c文件,在文件中定义串口接收buffer,编写串口IDLE中断处理函数,串口DMA发送函数,串口接收数据处理函数,软中断处理函数。

volatile uint8_t rx1_len = 0;  //Received data length
volatile uint8_t rec1_end_flag = 0; //Received data over flag
uint8_t rx1_buffer[BUFFER_SIZE]={0};  //Received data buffer

// IDLE中断处理
void Usart1_IDLE(void)
{
  uint32_t tmp_flag = 0;
  uint32_t temp;

  tmp_flag = __HAL_UART_GET_FLAG(&hlpuart1, UART_FLAG_IDLE);
  if ((tmp_flag != RESET))
  {
    __HAL_UART_CLEAR_IDLEFLAG(&hlpuart1);
    HAL_UART_DMAStop(&hlpuart1);
    temp = __HAL_DMA_GET_COUNTER(&hdma_lpuart1_rx);
    rx1_len = BUFFER_SIZE - temp;
    rec1_end_flag = 1;
    EXTI->SWIER1 |= 1 << 0;
  }
}
// 串口发送
int DMA_Usart1_Send(uint8_t *buf, uint8_t len)
{
  if (len == 0)
    return 0;
  HAL_StatusTypeDef sta = HAL_UART_Transmit_DMA(&hlpuart1, buf, len);
  if (sta != HAL_OK)
    return -1;
  return 0;
}
// 串口数据处理
void Usart1_Handle()
{
  if (rec1_end_flag) {
    DMA_Usart1_Send(rx1_buffer, rx1_len);
    rx1_len = 0;
    rec1_end_flag = 0;
    HAL_UART_Receive_DMA(&hlpuart1, rx1_buffer, BUFFER_SIZE);
  }
}
// 软中断处理
void EXTI0_1_IRQHandler(void)
{
  if (EXTI->RPR1 & 1 << 0)
  {
    EXTI->RPR1 |= 1 << 0;
    Usart1_Handle();
  }
}

打开stm32u0xx_it.c文件,找到void USART3_LPUART1_IRQHandler(void)函数,在函数体前部其中加入Usart1_IDLE函数调用。修改如下

/**
  * @brief This function handles USART3 (combined with EXTI 24) + LPUART1 global interrupt (combined with EXTI lines 28).
  */
void USART3_LPUART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART3_LPUART1_IRQn 0 */
  Usart1_IDLE();
  /* USER CODE END USART3_LPUART1_IRQn 0 */
  HAL_UART_IRQHandler(&hlpuart1);
  /* USER CODE BEGIN USART3_LPUART1_IRQn 1 */

  /* USER CODE END USART3_LPUART1_IRQn 1 */
}

打开main.c文件,在main()函数中,加入初始化软中断,开启IDLE中断,开启串口DMA接收的代码

  EXTI->IMR1 |= 1<<0;
  HAL_NVIC_SetPriority(EXTI0_1_IRQn, 3, 0);
  HAL_NVIC_EnableIRQ(EXTI0_1_IRQn);

  __HAL_UART_ENABLE_IT(&hlpuart1, UART_IT_IDLE);
  HAL_UART_Receive_DMA(&hlpuart1,rx1_buffer,BUFFER_SIZE);

7de69fa2edfa4f26aa98e1ffcb161291.png

配置非阻塞延时任务调度函数

编写任务调度函数

打开main.c文件,在user code的位置,定义task_scheduling函数。

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint8_t task_scheduling(uint32_t *task, uint32_t time)
{
  uint32_t sys_ms_timer = HAL_GetTick();
  if ((sys_ms_timer > *task) && ((sys_ms_timer - *task) > time))
  {
    *task = sys_ms_timer;
    return 1;
  }
  else if (*task > sys_ms_timer)
  {
    if (((0xffffffff - *task) + sys_ms_timer) > time)
    {
      *task = sys_ms_timer;
      return 1;
    }
  }
  return 0;
}

/* USER CODE END 0 */

延时任务创建

在主循环中,添加一个led灯闪烁的任务,每隔1000ms执行一次。

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    if (task_scheduling(&task1, 1000))
    {
      HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin);
    }

    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */

编译,下载与测试

编译

代码编写完成后,点击工具栏的build按钮 b55265e4c2d5402c98dcb3411183ba5b.png

下载

等待控制台完成编译后,控制台输出程序的空间占用,点击工具栏的下载按钮,等待完成下载。 f6bd7ab8a07c43818ceea8e5a3991dfa.png

测试

程序下载完成后,可以看到led灯每隔1s闪烁一下。 打开串口调试工具,发送数据,单片机将接收到的数据返回。 672b524d718744268b130ddad1a6fc47.png在这里插入图片描述

收藏 评论0 发布时间:2024-5-28 09:14

举报

0个回答
关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版