STM32H7开发笔记(0)——前言
0.为什么使用STM32H7
最近需要开发一个项目需要用到16位ADC,在STM32在线选型的网站上筛选了一下,发现只有部分型号的F3、H7具备16位ADC单元。在立创比了比价,F3系列最便宜的是F373CCT6,单价20;H7系列最便宜的是H750VBT6,单价19。看到这个价格的我直接震惊了,不理解但尊重,你敢卖这么便宜我就敢用这个开发。更详细的对比如下表所示,我的评价是没有理由不选H750VBT6:
STM32F373CCT6 | STM32H750VBT6 | |
---|---|---|
系列 | F3 | H7 |
架构 | Cortex-M4 | Cortex-M7 |
单价 | 20 | 19 |
Pin | 48 | 100 |
主频 | 72M | 480M |
Flash | 256K | 128K |
RAM | 32K | 1060K |
16位ADC单元 | 3 | 3 |
16位ADC通道 | 8 | 16 |
注:芯片价格为2024年11月08日查询,若时间跨度大则可能已经没有参考价值。
但是笔者从来没有接触过Cortex-M7架构(说的跟接触过Cortex-M4一样),所以这篇笔记主要目的就是浅显地了解一下Cortex-M7和H7。
官方手册:Datasheet
有意思的是ST官方(STM32H750超值系列 - 意法半导体STMicroelectronics)对H750系列的命名就是“超值”,确实超值(狗头
1.开发前瞻
1.1.关于HAL库
对于HAL库开发,当移植别人的程序时,一定要注意的是在烧录前一定要检查stm32h7xx_hal_conf.h
文件。找到HSE_VALUE
和LSE_VALUE
两个宏,将其值修改为自己使用晶振的值。
对于HAL库开发,与标准库区别很大的一点是中断。使用标准库开发时,需要编写中断服务函数;使用HAL库开发时,需要编写中断回调函数。
1.2.关于CubeMX
可以参考这两篇博客或其他博客。
在软件开发时,理论上来讲我们可以直接在CubeMX生成的文件中,把自己的代码写在/* USER CODE BEGIN */
和 /* USER CODE END */
之间,但是在网上看到有人遇到过BUG,在更改CubeMX文件后重新生成时,有可能会错误地覆盖掉代码。因此在用CubeMX使用Generate Code
自动生成代码后,自己新建一个Function
文件夹用于存放自己的代码,与CubeMX自动生成的区分开:
在Function
文件夹中添加自己需要的文件,必须添加的是mymain.c
、mymain.h
。
把CubeMX生成的main.c
中的所有内容,除了include
,全部复制到mymain.c
,同时在main.c
中把这部分代码注释掉。然后在SystemClock_Config();
之后,MX_GPIO_Init();
之前,配置一下delay_init
,括号里的是SYSCLK
的频率,注意不是HSE
的频率。mymain.c
内容如下:
#include "mymain.h"
void SystemClock_Config(void);
//......
int main(void)
{
HAL_Init();
SystemClock_Config();
//......
delay_init(480);
MX_GPIO_Init();
//......
while (1)
{
}
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Supply configuration update enable
*/
HAL_PWREx_ConfigSupply(PWR_LDO_SUPPLY);
/** Configure the main internal regulator output voltage
*/
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
__HAL_RCC_SYSCFG_CLK_ENABLE();
__HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE0);
while(!__HAL_PWR_GET_FLAG(PWR_FLAG_VOSRDY)) {}
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLM = 5;
RCC_OscInitStruct.PLL.PLLN = 192;
RCC_OscInitStruct.PLL.PLLP = 2;
RCC_OscInitStruct.PLL.PLLQ = 2;
RCC_OscInitStruct.PLL.PLLR = 2;
RCC_OscInitStruct.PLL.PLLRGE = RCC_PLL1VCIRANGE_2;
RCC_OscInitStruct.PLL.PLLVCOSEL = RCC_PLL1VCOWIDE;
RCC_OscInitStruct.PLL.PLLFRACN = 0;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2
|RCC_CLOCKTYPE_D3PCLK1|RCC_CLOCKTYPE_D1PCLK1;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.SYSCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.AHBCLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB3CLKDivider = RCC_APB3_DIV2;
RCC_ClkInitStruct.APB1CLKDivider = RCC_APB1_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_APB2_DIV2;
RCC_ClkInitStruct.APB4CLKDivider = RCC_APB4_DIV2;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
}
//......
mymain.h
则把那些头文件都复制过来:
#ifndef __MYMAIN_H
#define __MYMAIN_H
#include "main.h"
#include "memorymap.h"
#include "gpio.h"
//......
#include "delay.h"
#endif
2.Cortex-M7和H7
放一个对比图,等后面了解更加深入再回来补这一部分内容:
特性 | Cortex-M3 | Cortex-M4 | Cortex-M7 |
---|---|---|---|
架构 | ARMv7-M | ARMv7E-M | ARMv7E-M |
DSP 支持 | 无 | 有 | 有 |
浮点运算单元 | 无 | 单精度 | 单精度和双精度 |
缓存 | 无 | 无 | 支持(多级) |
主频范围 | 中等 | 中等 | 高(600 MHz 以上) |
典型应用 | 控制与任务管理 | 实时控制与信号处理 | 高性能嵌入式应用 |