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

【stm32WB55】外设快速入门

[复制链接]
银河帝国 发布时间:2023-7-23 17:49

工程模板文件的如何进行初始化

从cubemx软件找到本地保存包的地址。

image.png

image.png

image.png

本视频中作者对于STM32CubeMX的态度,只用来负责外设的初始化配置,将配置完成确认可以运行的代码再复制进工程中,这感觉也是不错的。

  1. 在文件浏览器中确定要移植的模板文件

    文件名称 文件地址
    CMSIS文件 Drivers文件夹下的Device和include
    HAL驱动库 STM32WBxx_HAL_Driver文件夹下的inc和src
    工程模板文件 \Projects\P-NUCLEO-WB55.Nucleo\Templates文件夹下的inc和src
  2. 复制到自己新建的模板文件夹下面,确定的格式是这样的 建立三个文件夹,命名如下Libraries,Project,Source

    文件夹名称 文件名称 文件含义
    Libraries CMSIS 官方库的CMSIS
    Libraries FWLIB 官方库的HAL部分
    Project 用于创建keil的地方
    Source App 保留
    Source Dev 保留
    Source Main 官方库的template
    Source Packages 保留

    注:

    1. 官方库的HAL部分,ll库可以不保留,初学阶段很少用到。
    2. 官方库的HAL部分,”...template“ 不保留,后续自己定义定时器的方法
    3. 官方模板文件中的”system_stm32wbxx.c“在CMSIS中有一份重复的了,放在template或者CMSIS都可以说得过去,只保留一份即可。
  3. 在keil中建立工程目录** image.png

  4. 注释,重新编译,0错误0警告,完成。 image.png image.png

  5. 替换掉main.h 加入新的system.h system.c 和 config.h 其中config.h的作用相当于新的main.h,包含所有用到的头文件,这样调试起来比较方便,system.h和system.c用来抽离main.c的代码,使main.c工程看起来更加清爽,模板文件中有main.c,xxx_it.c,xxx_msp_init.c文件用到了main.h头文件,把这些地方全都换成config.h,编译,完成。

  6. 记得执行一次save all,否则可能要收获一次血的教训。


GPIO外设 点亮LED灯

在参考资料UM2435中,可以看到套件中stm32WB55-Nucleo的大致介绍,

image.png

板载有三颗LED灯珠,从原理图中可以看到,分别接在PB5、PB0、PB1中。

image.png

用STM32CubeMX工具初始化外设驱动。开发板选择wb55-nucleo选项。

image.png

官方的开发板,默认的配置方案中就已经有了LED的脚位配置。

image.png

image.png

在外设的时钟可以看到,CPU1(M4内核)的时钟最高只有64MHz.

image.png

选定一个空的文件夹方便管理生成代码,选择对应的编译工具链,生成.c和.h的文件等

image.png

生成代码,用keil编译工具链先编译一趟看看有没有错误。

image.png

用hal库的接口编写最简单的闪灯程序,

HAL_GPIO_WritePin(LD1_GPIO_Port,LD1_Pin,GPIO_PIN_SET);
       HAL_GPIO_WritePin(LD2_GPIO_Port,LD2_Pin,GPIO_PIN_SET);
       HAL_GPIO_WritePin(LD3_GPIO_Port,LD3_Pin,GPIO_PIN_SET);
       HAL_Delay(500);
       HAL_GPIO_WritePin(LD1_GPIO_Port,LD1_Pin,GPIO_PIN_RESET);
       HAL_GPIO_WritePin(LD2_GPIO_Port,LD2_Pin,GPIO_PIN_RESET);
       HAL_GPIO_WritePin(LD3_GPIO_Port,LD3_Pin,GPIO_PIN_RESET);
       HAL_Delay(500);

烧录,复位测试,程序能够正常工作。

USART外设

实现printf功能

回到STM32CubeMX的配置界面,wb55-nucleo板子自带的usb串口连接在PB7和PB6上

image.png

串口外设和io口初始化由cubemx完成了,要实现一个printf功能用于后续程序调试,只需要实现fputc接口,重定向到串口上就行,

引入头文件

#include "stdio.h"

fputc实现

int fputc(int ch, FILE* file)
 {
     HAL_UART_Transmit(&huart1,(uint8_t*) &ch,1,200);
     return ch;
 }
 //main.c中做测试:
       printf(" hello world uart1 config ok!\r\n");
       HAL_Delay(1000);

keil编译器中,需要选择use MicroLIB选项,否则串口正常运行时不会有输出,只有在debug模式下,调试时才有串口打印。

image.png

实现完成后,使用串口可以看到输出,默认配置是数据位7位。

image.png

实现Rx接收中断功能

这里用的是标准库的实现思路简单验证。

STM32CubeMX的修改如下:

  1. 在STM32CubeMx中使能USART1外设的global interruptimage.png
  2. 修改中断的优先级,位置是在SystemCore的NVIC,可以先设置组优先级,再设置抢占和子优先级,stm32的优先级越大数字越小。image.png
  3. 完成,重新生成代码。

在KeilMDK软件中,stm32主要在 HAL_UART_MspInit底层初始化的函数中新增了以下的代码:

/* USART1 interrupt Init */
     HAL_NVIC_SetPriority(USART1_IRQn, 2, 0);
     HAL_NVIC_EnableIRQ(USART1_IRQn);

代码设置了优先级并且使能了串口的中断。

要实现串口的接收,我们主要需要修改串口中断服务函数,纯中断而且比较简单的实现是:使能两个标志位,RX空闲(IDLE)和RX非空(RXNE)标志位。

在外设数据寄存器非空标志位中,把数据从Rx外设搬运进数组。

在外设空闲标志位中,我们把数组的数据直接用printf打印出来。

  1. 使能两个标志位,位置可以是在MX_USART1_UART_Init函数中,cubemx并不帮我们生成这两个标志位。

    __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
     __HAL_UART_ENABLE_IT(&huart1,UART_IT_RXNE);
  2. 在串口的中断服务函数USART1_IRQHandler中,实现rx数据接收和回显。

    uint8_t g_usart1_rxbuf[240]; // global
     uint8_t g_usart1_rxidx = 0; // global
    
     //...
    
     uint8_t receive_char;
     if((__HAL_UART_GET_FLAG(&huart1, UART_FLAG_RXNE) != RESET))
     {
         __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_RXNE);
    
         HAL_UART_Receive(&huart1,&receive_char,1,200);
         g_usart1_rxbuf[g_usart1_rxidx ++] = receive_char;
     }
     if(( __HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) != RESET))
     {
         g_usart1_rxbuf[g_usart1_rxidx] = '\0';//c字符串的终止符号
         printf("usart1 receive:%s\n",g_usart1_rxbuf);
         g_usart1_rxidx = 0;
         __HAL_UART_CLEAR_FLAG(&huart1, UART_FLAG_IDLE);
     }
  3. 回显现象image.png


裸机用定时器实现简单的任务框架

在很多人介绍RTOS的时候,会笼统的把裸机说成是必须要延时等待长时间任务的,以此来说明其分时高效,不浪费cpu资源的特点。

裸机实现这样的特点是可行的,核心就是用定时器的定时功能。例如按键扫描,完全是可以放在1ms中断中通过软件的计数值进行按键的消抖,长短按的识别等等。但是没有框架,纯靠堆代码,会导致各种计数值和标志位飘散在程序中,看的凌乱而且难以管理。框架的引入可以大大节省各种命名的烦恼,保证程序的可靠性。

这个任务框架有点RTOS的味道主要由****task****multiTimer**两个文件组成,其中multiTimer是一个底层实现,有点类似用软件实现IIC,实际用户程序不应该使用这个底层。multiTimer实现了一个定时器链表,时钟滴答计时器可以放在systick中自增,每个while循环,将轮询定时器的超时时间,定时时间到了,执行回调函数,并且自动更新下一个超时时间。**

如何使用?

  1. 定义一个任务数组,这些任务统一一个时钟,如1ms,任务数组将在函数中进行初始化。

    #define TASK_TIMER_CNT  4   //任务定时器个数,增加任务时须修改该宏值
     struct Timer task_timer[TASK_TIMER_CNT];
  2. 声明回调函数

    void led0_task_timer_callback(void);//任务回调函数声明
     void led1_task_timer_callback(void);//任务回调函数声明
     void led2_task_timer_callback(void);//任务回调函数声明
     void uart_task_timer_callback(void);//任务回调函数声明
  3. 初始化定时器数组,其大小和任务数组相同且一一对应。

    task_timer_parameter task_timer_para[TASK_TIMER_CNT] =
      {
        /*{timeout_cb,       timeout(ms),    repeat(ms)}*/
        {led0_task_timer_callback, 1000,         1000},
        {led1_task_timer_callback, 500,          500},
        {led2_task_timer_callback, 200,          200},
          {uart_task_timer_callback, 1000,               1000},
        /*{timeout_cb,       timeout(ms),    repeat(ms)}*/ 
      };
  4. 任务定时器回调函数实现

    void led0_task_timer_callback(void)//任务回调函数实现
     {
       led0_toggle();
    
     }
    
     void led1_task_timer_callback(void)//任务回调函数实现
     {
       led1_toggle();
    
     }
     void led2_task_timer_callback(void)//任务回调函数声明
     {
         led2_toggle();
     }
     void uart_task_timer_callback(void)
     {
         printf("hello world\r\n");
     }
  5. 任务初始化函数

    int task_init(void)
     {   
       uint8_t task_index;
       for(task_index = 0;task_index < TASK_TIMER_CNT;task_index++)
       {
           timer_init(&task_timer[task_index],
                    task_timer_para[task_index].timeout_cb, 
                    task_timer_para[task_index].timeout,
                    task_timer_para[task_index].repeat); 
    
           timer_start(&task_timer[task_index]);
       }
       return 0;
     }
  6. 任务正常循环调用

    int task_run(void)
     {
       timer_loop();
       return 0;
     }

要添加任务,仅需要修改任务数的宏,声明和定义一个新的函数,然后设定多少ms执行一次即可。

小结

主要用wb55实现了最基础的外设驱动和功能。wb55的外设驱动起来还是比较舒服的,没有碰到太多的坑。

由于不熟悉hal库,有参考一些视频资料, 本文的代码和思路很大部分参考的一位b站大佬:hezhijie157,视频地址: STM32-HAL库速讲**哔哩哔哩bilibili**

代码资料:链接:https://pan.baidu.com/s/1AnxLqKJnwHRSpT6cIMzV-A提取码:3mgo

image.png
image.png
收藏 评论0 发布时间:2023-7-23 17:49

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版