使用VScode开发环境点亮LED
1.搭建VScode的STM32开发环境
(1)下载安装 VScode,官网地址:
自行安装,网上教程丰富!
(2)在VScode商店中安装chinses简体中文插件,Embedded IDE插件及所需的工作环境插件 C/C++ 、C/C++ Extension Pack、C/C++ Themes
(3)打开Embedded IDE :官方论坛链接
设置工具链 指定 Keil C51和Keil MDK的路径,及自己安装的Keil路径。我安装了两个软件在同一路径下(可以同时开发两种内核的MCU)所以指定两次。在这里插入图片描述
如截图工具链设置成功后面会出现对号。
2.导入项目
选择MDK选项,导入之前建立的工程模板
根据提示选择信任及工作平台,即可加载文件完成,如下图:选择编译器及内核
打开main.c文件单击右上角 构建图标(F7)或工程列表区域构建图标,进行编译。
下方终端提示如下信息编译完成。
- 点亮LED灯
在HARDWARE文件夹中添加LED.C文件
EIDE项目栏——项目属性——包含目录中单击+添加LED.H的文件路径。
主函数部分如下:
#include "sys.h"
#include "delay.h"
#include "led.h"
int main(void)
{
Cache_Enable(); //打开L1-Cache
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(160,5,2,4); //设置时钟,400Mhz
delay_init(400);
LED_Init();
while(1)
{
delay_ms(500); //延时500ms
LED1_Toggle; //翻转LED电平
}
}
Cache_Enable(); //打开L1-Cache
开启功能
//使能CPU的L1-Cache
void Cache_Enable(void)
{
//MPU_Config();
SCB_EnableICache();//使能I-Cache
SCB_EnableDCache();//使能D-Cache
SCB->CACR|=1<<2; //强制D-Cache透写,如不开启,实际使用中可能遇到各种问题
}
Stm32_Clock_Init(160,5,2,4); //设置时钟,400Mhz 重新设定系统时钟 为400Mhz
在led.c文件中
定义输出的引脚
#include "led.h"
//LED IO初始化
void LED_Init(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOC_CLK_ENABLE(); //开启GPIOC时钟
__HAL_RCC_GPIOH_CLK_ENABLE(); //开启GPIOC时钟
GPIO_Initure.Pin=GPIO_PIN_9; //PC9
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH; //高速
HAL_GPIO_Init(GPIOC,&GPIO_Initure); //初始化GPIOC9
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_SET); //PC9置1
GPIO_Initure.Pin=GPIO_PIN_12; //PC9
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽输出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_FREQ_VERY_HIGH; //高速
HAL_GPIO_Init(GPIOH,&GPIO_Initure); //初始化GPIOB.0和GPIOB.1
HAL_GPIO_WritePin(GPIOH,GPIO_PIN_12,GPIO_PIN_RESET); //PB1置1
}
GPIO_InitTypeDef GPIO_Initure; 为GPIO结构体句柄;
使用外设之前应对外设的时钟进行使能,HAL库定义了完整的时钟函数:__HAL_RCC_GPIOC_CLK_ENABLE(); //开启GPIOC时钟
对于H7系列的GPIO具有8种工作模式,可以详见H7系列参考手册:一般输出功能可以选择具有上拉功能的推挽输出。
#define LED1(n) (n?HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_SET):HAL_GPIO_WritePin(GPIOC,GPIO_PIN_9,GPIO_PIN_RESET))
#define LED1_Toggle (HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_9)) //LED1输出电平翻转
H7系列不支持位带操作,定义LED1(n) 使用三目运算对单一引脚进行赋值,本质是调用HAL库的void HAL_GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_PinState PinState)函数;
同时HAL库还提供了电平翻转函数oid HAL_GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);
编译工程 提示没有错误即可下载!
EIDE 支持多种下载工具:烧录配置:——选择自己的下载工具
1JLink: 打开插件设置指定JLink安装路径;
配置芯片型号、接口类型、下载速度。
2.STLink:指定ST-LINK Utility软件安装路径:
3OpenOCD:指定OpenOCD的安装路径:
芯片配置;工具链;
4.下载程序验证
点击 烧录按键下载程序。
使用定时器控制LED频闪
STM32H7系列单片机设计有基本定时器 TIM6、TIM7。基本定时器 TIM6 和 TIM7 包含一个 16 位自动重载计数器,该计数器由可编程预分频器驱动。此类定时器不仅可用作通用定时器以生成时基,还可以专门用于驱动数模转换器 (DAC)。实际上,此类定时器内部连接到 DAC 并能够通过其触发输出驱动 DAC。这些定时器彼此完全独立,不共享任何资源。
首先要明白STM32H7系列的时钟架构:可以参考STM32H7新版系统框图
查询数据总线结构,我们发现 TIM6挂在APB1的总线上,在看时钟系统、在系统主频率为400MHz的情况下APB1总线的定时器时钟为200MHz。
由此可以配置定时周期为10ms的工作模式。
#include "timer.h"
#include "led.h"
TIM_HandleTypeDef TIM6_Handler; //定时器句柄
//基本定时器6中断初始化,定时器6在APB1上,APB1的定时器时钟为200MHz
//arr:自动重装值。
//psc:时钟预分频数
//定时器溢出时间计算方法:Tout=((arr+1)*(psc+1))/Ft us.
//Ft=定时器工作频率,单位:Mhz
//这里使用的是定时器6!(定时器6挂在APB1上,时钟为HCLK/2)
void TIM6_Init(u16 arr,u16 psc)
{
TIM6_Handler.Instance=TIM6; //定时器6
TIM6_Handler.Init.Prescaler=psc; //分频
TIM6_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器
TIM6_Handler.Init.Period=arr; //自动装载值
TIM6_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子
HAL_TIM_Base_Init(&TIM6_Handler);
HAL_TIM_Base_Start_IT(&TIM6_Handler); //使能定时器6和定时器6更新中断:TIM_IT_UPDATE
}
//定时器底册驱动,开启时钟,设置中断优先级
//此函数会被HAL_TIM_Base_Init()函数调用
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM6)
{
__HAL_RCC_TIM6_CLK_ENABLE(); //使能TIM6时钟
HAL_NVIC_SetPriority(TIM6_DAC_IRQn,15,0); //设置中断优先级,抢占优先级15,子优先级0
HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn); //开启ITM6中断
}
}
//定时器6中断服务函数
void TIM6_DAC_IRQHandler(void)
{
HAL_TIM_IRQHandler(&TIM6_Handler);
}
#include "sys.h"
#include "delay.h"
#include "led.h"
#include "timer.h"
int main(void)
{
Cache_Enable(); //打开L1-Cache
HAL_Init(); //初始化HAL库
Stm32_Clock_Init(160,5,2,4); //设置时钟,400Mhz
delay_init(400);
LED_Init();
TIM6_Init(100-1,20000-1); //定时器时钟 200M 分频系数 2000-1; 200M-20000=10k 自动重装载 100-1 定时周期 10ms 1/10k*100= 10ms
while(1)
{
}
}
u8 timer_flags=0;
//定时器中断服务回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM6)
{
timer_flags++;
if(timer_flags==50)
{
LED1_Toggle; //LED1反转
timer_flags=0;
}
}
}
HAL_NVIC_SetPriority(TIM6_DAC_IRQn,15,0); //设置中断优先级,抢占优先级15,子优先级0
中断优先级的设定。
在 HAL_Init(void);函数中设定 4 位抢占优先级, 0 位响应优先级
/* Set Interrupt Group Priority */
HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4);
IP[240]:全称是: Interrupt Priority Registers,是一个中断优先级控制的寄存器组。这个寄
存器组相当重要! STM32H743 的中断分组与这个寄存器组密切相关。 IP 寄存器组由 240 个 8bit
的寄存器组成,每个可屏蔽中断占用 8bit,这样总共可以表示 240 个可屏蔽中断。 而 STM32H743
只用到了其中的 150 个。 IP[149]~IP[0]分别对应中断 149~0。 而每个可屏蔽中断占用的 8bit 并没
有全部使用,而是 只用了高 4 位。这 4 位,又分为抢占优先级和子优先级。抢占优先级在前,
子优先级在后。而这两个优先级各占几个位又要根据 SCB->AIRCR 中的中断分组设置来决定。
这里简单介绍一下 STM32H743 的中断分组: STM32H743 将中断分为 5 个组,组 0~4。该
分组的设置是由 SCB->AIRCR 寄存器的 bit10~8 来定义的。
组 | AIRCR[10: 8] | bit[7: 4]分配情况 | 分配结果 |
---|---|---|---|
0 | 111 | 0: 4 | 0 位抢占优先级, 4 位响应优先级 |
1 | 110 | 1: 3 | 1 位抢占优先级, 3 位响应优先级 |
2 | 101 | 2: 2 | 2 位抢占优先级, 2 位响应优先级 |
3 | 100 | 3: 1 | 3 位抢占优先级, 1 位响应优先级 |
4 | 011 | 4: 0 | 4 位抢占优先级, 0 位响应优先级 |
通过这个表,我们就可以清楚的看到组 0~4 对应的配置关系, 例如组设置为 3,那么此时所有的 108 个中断,每个中断的中断优先寄存器的高四位中的最高 3 位是抢占优先级,低 1 位是响应优先级。每个中断, 你可以设置抢占优先级为 0~7,响应优先级为 1 或 0。抢占优先级的级别高于响应优先级。而数值越小所代表的优先级就越高。
void TIM6_DAC_IRQHandler(void) 是TIM6的中断服务函数可以在startup_stm32h743xx.s文件中的中断向量表中找到。
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) 是HAL库封装的特点,所有的定时器中断程序都可以通过
void HAL_TIM_IRQHandler(TIM_HandleTypeDef *htim);函数对其调用 因此所有的逻辑程序都可以在里面进行执行;
本文 执行500ms 对LED的状态改变一次。