💽前言
本文为一份简单入门笔记,以 stm32 单片机为例。
**声明:**由于笔者不是专业人员,仅作为入门初学者,因此本文避免不了的有专业性错误,请自行甄别。
完全以一位纯应用型软件工程师的角度进行学习和思考。
嵌入式软件还是遵循软件的本质:基于下层的支持为上层提供应用服务。
只是我们目前互联网上的大多数软件是基于操作系统或者虚拟机或解释器等等基础。
而单片机是直接操作硬件,是直接基于硬件提供的功能。
💽软件框架
**声明:**本文以软件架构分析为核心。
📀工具环境
关键和工具不是本文重点。
基于 STM32CubeMX 图形化配置硬件。
使用 Keil 进行软件开发。
📀模板工程
使用 CubeMX 生成的框架架构
工程名:20240824
- .ioc & .mxproject
- CubeMX 工程文件
- Core
- 硬件信息生成的代码框架
- 特别注意
system_stm32f4xx.c
在 Keil 中是显示在 Drivers 文件夹中- Drivers
- 具体对应硬件的驱动代码
- CMSIS
- 基于硬件芯片原厂架构的文件(基于芯片的最底层)
- STM32 xxx
- 基于 CMSIS 的二次开发
- 一般属于暴露给用户开发的接口- MDK-ARM
- .uvprojx & .uvoptx
- 对用 Keil 的工程文件
startup_stm32f407xx .s
- 默认配置的汇编文件
C:.
│ .mxproject
│ 20240824.ioc
│
├─Core
│ ├─Inc
│ │ gpio.h
│ │ main.h
│ │ stm32f4xx_hal_conf.h
│ │ stm32f4xx_it.h
│ │
│ └─Src
│ gpio.c
│ main.c
│ stm32f4xx_hal_msp.c
│ stm32f4xx_it.c
│ system_stm32f4xx.c
│
├─Drivers
│ ├─CMSIS
│ │ ├─Core
│ │ ├─Core_A
│ │ ├─DAP
│ │ ├─Device
│ │ ├─Documentation
│ │ ├─DSP
│ │ ├─Include
│ │ ├─NN
│ │ ├─RTOS
│ │ └─RTOS2
│ │ LICENSE.txt
│ └─STM32F4xx_HAL_Driver
│
└─MDK-ARM
20240824.uvoptx
20240824.uvprojx
startup_stm32f407xx.s
📀编译后
在编译后会在 MDK-ARM
文件夹下生成编译的中间文件:
其中 20240824 是项目名。其中同名文件夹下的是最核心的中间文件,其中包含可烧文件。
C:.
│ 20240824.uvguix.windows
│ 20240824.uvoptx
│ 20240824.uvprojx
│ startup_stm32f407xx.lst
│ startup_stm32f407xx.s
│
├─20240824
│
├─DebugConfig
│
└─RTE
可烧入文件
axf:包含数据,地址,代码,debug调试等信息,可以用下载器直接烧到板子上
hex:包含数据,地址信息
bin:包含数据(最终都是转到bin)
因此一般文件大小为
.bin < .hex < .axf
📀Code
注意:由于是使用 CubeMX
生成的,因此可能后续会有硬件的修改,因此 CubeMX
会在代码中注入注释表示。
即在下面形式的代码区间内编写代码才不会被下次重新生成框架代码所移除。
/* USER CODE BEGIN Includes */
// 自己的代码
/* USER CODE END Includes */
但由于本文是初学的学习笔记,因此本篇文章中展示的内容无视该规则。
main
下面是 main 的主题逻辑,核心就是 初始化-服务轮询
架构。
int main(void) {
/**
* Reset of all peripherals, Initializes the Flash interface and the Systick.
* 重置所有外设,初始化Flash接口和系统棒。
* =========================================================================
* hal是stm32的三种不同应用库的一种
*
*/
HAL_Init();
/**
* Configure the system clock
* 配置系统时钟
* ==========================
* 系统(硬件)总体(所有)的时钟
* 相关的配置和初始化,相当于一个总开关
* 保证芯片是正常能跑起来的
*/
SystemClock_Config();
/**
* Initialize all configured peripherals
* 初始化所有配置的外设
* =====================================
* 在 CubeMX 中进行配置的外设专门的定制化初始化
* 如:定时器,串口等
*/
MX_GPIO_Init();
MX_TIM1_Init();
MX_USART1_UART_Init();
MX_USART2_UART_Init();
/* USER CODE BEGIN 2 */
/**
* 业务代码
* 一般为业务的初始化
*/
myservice_init();
/**
* 执行 while(1) 轮询
* 一般为业务的状态机
*/
while (1) {
myservice_state();
}
}
中断
在 stm32f4xx_it.c/h
文件中,是中断处理的函数,如下面的形式:
#ifndef __STM32F4xx_IT_H
#define __STM32F4xx_IT_H
#ifdef __cplusplus
extern "C" {
#endif
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
void USART2_IRQHandler(void);
#ifdef __cplusplus
}
#endif
#endif /* __STM32F4xx_IT_H */
在 MDK-ARM/startup_stm32f407xx.s
文件中可看到映射,也就是说这些是框架直接帮我们做了,不用我们考虑链接的问题。
__Vectors DCD __initial_sp ; Top of Stack
DCD Reset_Handler ; Reset Handler
DCD NMI_Handler ; NMI Handler
DCD HardFault_Handler ; Hard Fault Handler
DCD MemManage_Handler ; MPU Fault Handler
DCD BusFault_Handler ; Bus Fault Handler
DCD UsageFault_Handler ; Usage Fault Handler
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD 0 ; Reserved
DCD SVC_Handler ; SVCall Handler
DCD DebugMon_Handler ; Debug Monitor Handler
DCD 0 ; Reserved
DCD PendSV_Handler ; PendSV Handler
DCD SysTick_Handler ; SysTick Handler
📀典例举例
通过 MX_GPIO_Init()
了解嵌入式的本质,就是配置硬件操作。
void MX_GPIO_Init(void) {
GPIO_InitTypeDef GPIO_InitStruct = {0};
/**
* GPIO Ports Clock Enable
* GPIO端口时钟使能
* ======================
* 开了C H A 三个组的时钟
* 仅是启动open,还没用
* 如果没开,即使下面初始化成功了,硬件还是没反应
*/
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
/**
* Configure GPIO pin Output Level
* 配置GPIO引脚输出电平
* ================================
* GPIOC 引脚*组*的地址(C组)
* GPIO_PIN_14 引脚号
* GPIO_PIN_RESET 表示低电平
*/
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_RESET);
/**
* Configure GPIO pin : PC14
* 配置GPIO引脚:PC14
* =========================
* 配置这个引脚的基本参数
*/
GPIO_InitStruct.Pin = GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
然后具体的业务操作,一个典型的应用就是:
-
在
myservice_init();
对串口的buffer进行初始化, -
在
myservice_state();
对缓冲区进行状态机的处理。
这段数据业务逻辑,不做具体展示了。一般来说串口接受数据就是靠上文中介绍的中断函数。(当然也有办法不通过中断操作)
💽Keil 使用notes
添加头文件路径
默认配置下是无头文件相关的内容的
添加头文件
字符集设置
添加一个组
这个组是只针对工程,而不是外部的具体文件和文件夹(不会自动识别)
配置烧写器
⭐END
🌟关注我
关注我,学习更多C/C++,算法,计算机知识
B站:
👨💻主页:天赐细莲 bilibili