通过RT-Thread提供抽象接口层驱动硬件(全功能版本工程才有); |
通用性概念参考Linux系统框架/内核驱动篇; |
RT-Thread Studio添加cubemx路径: |
一、配置烧写方式/时基
System Core->SYS: 注意在裸机代码中如果Debug引脚不进行配置的话,烧写一次程序后板子将不能进行第二次烧写: |
二、配置时钟输入/时钟树(RCC)
1、时钟使能概述
为了节省功耗,STM32系列芯片专门设置相关寄存器来控制每个片内外设模块的使能和关闭(默认情况下是关闭状态); 所有在配置相关模块之前需要是先使能模块对应的时钟,否则模块寄存器的配置是无效的; |
![]() |
2、时钟树
参考对应芯片数据手册。 |
具体参考对应芯片数据手册: |
![]() |
外部高速时钟(HSE): |
芯片外接一个脉冲发生器、计数器、振荡器等,频率大小范围:4~16,常用8M; 作为锁相环倍频器时钟(PLLCLK)的备选时钟源; 作为实时时钟RTC模块的备选时钟源; 作为输出时钟信号的备选时钟源; 作为系统时钟(SYSCLK)的备选时钟源; |
外部低速时钟(LSE): |
芯片外接一个32.768KHz的振荡器; 作为实时时钟RTC模块的备选时钟源; |
内部高速时钟(HSI): |
芯片内部集成的一个8M大小的RC振荡器; 作为锁相环倍频器时钟(PLLCLK)的备选时钟源; 作为系统时钟(SYSCLK)的备选时钟源; 作为输出时钟信号的备选时钟源; |
内部低速时钟(LSI): |
芯片内部集成一个40KHz大小的RC振荡器; 作为实时时钟RTC模块的备选时钟源; 作为独立看门狗模块的时钟源; |
系统时钟(SYSCLK):STM32芯片工作频率时钟,由内部高速时钟(HSI)、外部高速时钟(HSE)以及锁相环倍频时钟(PLLCLK)三者任选其一提供; AHB总线时钟:AHB总线时钟为高性能设备间通信总线时钟,一般称为HCLK; APBx总线时钟:总线桥设备时钟,分为两种:分别是APB1低速设备时钟(PCLK1)以及高速设备时钟(PCLK2); |
3、配置时钟输入
System Core->RCC: (一般没有用到RTC实时时钟模块就不使能LSE) 注意这里按需选择是有源晶振/无源晶振: |
4、配置时钟树
根据对应芯片具体设置: F103RCT6: F407ZET6: |
三、片上驱动配置
1、片上驱动
具体参考片上驱动部分。 |
默认给一个USART1配置(异步),不然会报错: |
2、NVIC
- NVIC中断控制器
在Cortex-M内核CPU中集成一个中断硬件管理器; NVIC是arm内核中断管理控制器的简称,主要管理芯片上的中断处理功能; 中断控制器作用: 接收中断请求,判断优先级高低,指挥CPU按先后顺序处理中断源; 参考对应芯片手册; |
- 中断优先级/嵌套
- 中断概念
当内核在正常运行程序过程中,由于内部或者外部的某些原因,暂时中止当前正在运行的程序,转去执行请求内核为其服务的那个外设或者事件; 等待该程序执行完成之后,返回中止的地方继续往下执行,这个过程就叫做中断; |
- 中断源概念
中断源是指打断当前程序的事件或者原因,称为中断源; |
- 中断执行过程
- 中断使用意义
使用非阻塞的方法,可以提高CPU的效率,一般情况下,主动完成的事情需要使用中断,被动完成的事情需要使用中断; 如果有多个中断源,根据中断的优先级,对处理中断源的顺序进行排序 -- 优先级等级; |
NVIC控制器中断优先级分类: | ||||
人为优先级: | ||||
又称为可编程优先级,通过设置NVIC控制的寄存器来实现对NVIC中的优先级设置。 | ||||
抢占优先级:决定不同等级之间的中断是否可以进行嵌套,高优先级的中断可以打断低优先级的中断,等级数字越小,优先级越高; 响应优先级:又被称为子优先级,响应优先级的作用是当多个中断源同时产生,并且抢占优先级相同的时候,CPU先执行响应优先级高的中断请求,等级数字越小,优先级越高。 | ||||
自然优先级: | ||||
又称为固定优先级,生产时已固定分配好的中断序列号,中断优先级所代表的十进制数字越小,优先级越高; | ||||
优先级等级总结: | ||||
抢占优先级>响应优先级>自然优先级; | ||||
抢占优先级决定是中断是否可以嵌套; | ||||
响应优先级和自然优先级是决定当多个中断源同时发生的时候,优先执行谁(如果抢占优先级相同,判断响应优先级,如果响应优先级也相同,继续判断自然优先级)。 | ||||
NVIC控制器中断源优先级: | ||||
Cortex-M处理器NVIC使用8个位来表示中断优先级的等级(包含抢占优先级和响应优先级); 具体抢占优先级和响应优先级各占用多少位需要根据设置的优先级分组决定; 优先级十进制数越小,优先级越高; | ||||
在地址为0xE000ED0C这个寄存器(应用程序中断及复位控制寄存器(AIRCR))的10~8 位设置优先级分组: | ||||
优先级分组 | 中断优先级分组说明 | 抢占优先级范围 | 响应优先级范围 | 寄存器优先级分组写入值 |
第0组 | 所有4个位用于指定响应优先级 | 0 | 0~15 | 0x07 |
第1组 | 最高1位用于指定抢占优先级,最低3位用于指定响应优先级 | 0~1 | 0~7 | 0x06 |
第2组 | 最高2位用于指定抢占优先级,最低2位用于指定响应优先级 | 0~3 | 0~3 | 0x05 |
第3组 | 最高3位用于指定抢占优先级,最低1位用于指定响应优先级 | 0~7 | 0~1 | 0x04 |
第4组 | 所有4个位用于指定抢占优先级 | 0~15 | 0 | 0x03 |
优先级分组设定方法: 确定抢占优先级和响应优先级; 根据确定好的优先级确定抢占优先级位数和响应优先级的位数; 确定优先级分组编码 = 7 - 你要设置的抢占优先级的位数。 注:在一个工程内只允许有一种中断优先级分组情况,确定了中断优先级分组,就是确定了中断优先级的等级个数,通常中断优先级分组写在主函数硬件初始化之前。 |
- 中断向量表
具体中断异常向量表,参考对应芯片手册; |
位置:中断源编号值在“stm32fxx.h”头文件中定义; |
优先级:自然优先级等级编号; |
地址:中断源中断入口地址; 异常向量表中的中断入口地址在“startup_stm32fxxx.s”启动文件中定义,这些定义好的中断入口地址编号代表的是对应中断源的中断服务函数入口地址。 |
3、DMA
与CPU共用AHB总线,内部具有协调器,保证CPU至少获得50%以上的AHB总线时间; DMA是直接存储访问存取(Driect Memory Access)的简称,主要用来提供外设(USART、定时器、ADC)到存储器(SRAM、Flash)、存储器到外设、存储器到存储器的高速数据传输。在无须CPU干预的情况下,数据可以通过DMA快速移动; DMA模块同一时刻内只能工作一个通道,若通道优先级相同,则按通道编号决定通道优先级(通道号数字越小,等级越高); 数据传输宽度:8bit、16bit、32bit; 支持循环工作模式; 中断源:传输过半、传输完成、传输错误; 工作周期传输数量范围:1~65536。 |
DMA控制器的仲裁器主要是判断DMA控制器通道之间的优先级,解决当DMA控制器查询到多个通道同时产生DMA请求时,DMA控制器优先执行的依据。 软件优先级:软件优先级又称为人为优先级,主要是通过配置DMA寄存器来对每个通道进行优先级的分配: 非常优先级; 高优先级; 中优先级; 低优先级。 硬件优先级:硬件优先级又称为自然优先级,当两个通道的DMA请求具有相同软件优先级(人为优先级)时,则编号低的通道的优先级高于编号高的通道。 注:DMA控制器的优先级不存在高优先级打断低优先级的情况,只作为当多个通道之间同时发送了DMA传输请求时,DMA控制器优先响应那个通道的判断依据。 |
4、SysTick
SysTick定时器也称为系统滴答定时器或系统定时器,是Cortex-M系列处理器内核集成的一个简单的定时器,异常编号(中断编号)为“15”。 SysTcik定时器有两个时钟源,分别是内部时钟(FCLK自由运行时钟,等于系统总线时钟HCLK)和外部时钟(STCLK Cotrtex系统定时器时钟,等于系统总线时钟8分频之后时钟)。 |
![]() |
SysTick定时器是一个24位(计算范围0~224-1)倒计数(计数方式为减法)定时器,从预装载值(计数周期)一直计数到0,然后再从重装载寄存器中自动重装载初始值,只要不把SysTick定时器的使能位清除,那么SysTick定时器就永远不停,即使芯片在睡眠模式下也能正常工作。 |
![]() |
时钟源选择: 外部时钟: AHB总线经过8分频后给系统滴答定时器提供的9MHz时钟源; 内核时钟: FCLK时钟源给系统滴答定时器提供的72MHz。 |
RT-Thread嘀嗒定时器启用(drivers/drv_common.c): 实际裸机调用:SysTick_Config() |
RT-Thread嘀嗒定时器中断服务函数(drivers/drv_common.c): |
四、修改输出工程方式
五、修改生成文件方式
六、生成代码
生成完毕关闭CubeMX即可。 |
代码生成后回到RT-Thread Studio工程,提示stm32fxxx_hal_conf.h改为stm32fxxx_hal_conf_bak.h备份文件,是因为conf内存储着stm32的hal库配置信息,RT-Thread做了一个备份,不用管; |
七、SConscript文件
SConscript 文件可以控制源码文件的加入,并且可以指定文件的 Group(与 MDK/IAR 等 IDE 中的 Group 的概念类似); SCons 提供了很多内置函数可以帮助我们快速添加源码程序,利用这些函数,再配合一些简单的 Python 语句我们就能随心所欲向项目中添加或者删除源码,常用函数: GetCurrentDir() 获取当前路径; Glob(’*.c’) 获取当前目录下的所有 C 文件。修改参数的值为其他后缀就可以匹配当前目录下的所有某类型的文件; GetDepend(macro) 该函数定义在 tools 目录下的脚本文件中,它会从 rtconfig.h 文件读取配置信息,其参数为 rtconfig.h 中的宏名。如果 rtconfig.h 打开了某个宏,则这个方法(函数)返回真,否则返回假; Split(str) 将字符串 str 分割成一个列表 list; DefineGroup(name, src, depend,**parameters) 这是 RT-Thread 基于 SCons 扩展的一个方法(函数),DefineGroup 用于定义一个组件,组件可以是一个目录(下的文件或子目录),也是后续一些 IDE 工程文件中的一个 Group 或文件夹; |
新建SConscript文件: |
# 引入os模块 import os # 导入building的所有模块 from building import * # 获取获取当前路径,并保存至变量cwd cwd = GetCurrentDir() #获取当前目录下的所有 C 文件,并保存至src变量 src = Glob('*.c') #由于RT-Thread工程中存在部分相同函数文件,所以对src重新赋值 # add cubemx drivers # 文件中的stm32f4xx_it.c、 system_stm32f4xx.c不加入构建 # 其余文件按相同格式填写到下述括号内除非直接调里面函数,否则只需要复制相关MSP初始化到board.c src = Split(''' Src/stm32f4xx_hal_msp.c Src/main.c ''') # 创建路径列表,并保存至path中 path = [cwd] path += [cwd + '/Inc'] # RT-Thread 基于 SCons 扩展的一个方法(函数) group = DefineGroup('cubemx', src, depend = [''], CPPPATH = path) Return('group') |
保存文件,右键空白处更新软件包: |
八、重新构建工程
没有出现错误,如果出现错误,则大概率是函数重复定义导致,比如multiple definition of main; 则找到cubemx文件夹下的main函数,增加弱定义__weak,再次构建则不会报错; |
检查时钟配置,打开drv_clk.c,RT-Thread Studio已自动将CubeMx时钟配置更新到RT-Thread工程中: 如果没有正常的更新过来,需要将cubemx中main.c中的void SystemClock_Config(void)函数代码复制到drv_clk.c中的void system_clock_config(int target_freq_mhz)函数中; |
注: 如果出现Error_Handler错误,需要将drv_common.h中的宏定义注释: //#ifndef Error_Handler //#define Error_Handler() _Error_Handler(__FILE__, __LINE__) //#endif 然后在drv_clk.c中包含main.h头文件 |
九、调用CubeMX函数
将cubemx中main.c中头文件和相关函数复制到applications-main.c-main函数中即可,其他依此类推; |