一.软件准备
1.STM32CubeMX
生成我们所需要的工程文件
2.STM32CubeCLT
包含cmake等工具包
3.Vscode
对代码进行修改和编写
二.操作过程
首先我们打开STM32CubeMX

在搜索框里面输入STM32F103C8T6这个使我们第一块接触的芯片

双击进入到它的里面

这里我们设置Debug模式
设置成Serial Wire模式,也就是我们常说的SWD模式
具体有关这个模式可以看这个文章深入理解 ARM Serial Wire Debug (SWD) 协议_swd协议-CSDN博客

左键这个芯片图选择芯片的输出模式
这里为了简化我就初始化六个引脚

从上图上可以看出我们的PA6,PA7,PB0被初始化成输出引脚
而我们的PA8,PB15,PB14被初始化成输入引脚
我们可以从输入引脚获取输入信号,例如使用按键控制LED灯,按键就是输入信号
这个是比较简单的

点击这个创建工具生成我们的HAL库代码,在这个里面就完成标准库的初始化
比如我们可能在标准库里写
cpp
/*开启时钟*/
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
//使用各个外设前必须开启时钟,否则对外设的操作无效
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure; //定义结构体变量
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //GPIO模式,赋值为推挽输出模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; //GPIO引脚,赋值为第6和第7号引脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //GPIO速度,赋值为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //将赋值后的构体变量传递给GPIO_Init函数
//函数内部会自动根据结构体的参数配置相应寄存器
//实现GPIOA的初始化
大概就这这一大堆东西他直接给你写好
然后进入

之后弹出这个直接关闭就行

然后我们进入VScode (如果第一次使用vscode安装Chinese中文包)
在vscode中新建一个配置环境

点击小齿轮

安装STM32Cube for vscode
和c/c++ Extension Pack 和c/c++ Themes
还有CMake工具
然后进入源码
main就是我们标准库中的主函数
找下面的路径

这个就是他给我们生成的代码文件
我们可以发现他给我们初始化好了GPIO
我们可以从这个函数中看出来 MX_GPIO_Init中
objectivec
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* USER CODE BEGIN MX_GPIO_Init_1 */
/* USER CODE END MX_GPIO_Init_1 */
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6|GPIO_PIN_7, GPIO_PIN_RESET);
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
/*Configure GPIO pins : PA6 PA7 */
GPIO_InitStruct.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pin : PB0 */
GPIO_InitStruct.Pin = GPIO_PIN_0;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pins : PB14 PB15 */
GPIO_InitStruct.Pin = GPIO_PIN_14|GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/*Configure GPIO pin : PA8 */
GPIO_InitStruct.Pin = GPIO_PIN_8;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* USER CODE BEGIN MX_GPIO_Init_2 */
/* USER CODE END MX_GPIO_Init_2 */
}
在main中的while(1)中写逻辑
点击下面那个生成就生成工程文件了,就可以烧录到我们的STM32F103C8T6上了
但是这个里面我发现下面的代码
objectivec
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
这个在标准库里面是没有见过的
__disable_irq(); 禁止所有中断
while(1){
} 如果出现错误将会停在这里
但是这里有引入一个新的概念,什么是句柄Handle,什么时候调用句柄,是我们自己调用还是系统自动调用。
在源码中我看到这里调用了句柄
objectivec
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
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_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_0) != HAL_OK)
{
Error_Handler();
}
}
这段代码中调用了这个句柄
看出这个应该是我们自己调用的,而不是系统判断自己调用的
那这个句柄和函数有啥区别呢
在deepseek中我了解到
句柄相当于这个引用type & error 就是一旦出现这个问题就是这个错误
而且句柄在这个函数中有唯一性