STM32HAL库全网最详细笔记——B站铁头山羊

发布时间:2026/10/5 2:23:23
STM32HAL库全网最详细笔记——B站铁头山羊 STM32HAL库个人整理最全笔记——B站铁头山羊引脚的分布与命名型号STM32F103C8T6引脚普通IO引脚蓝灰色可编程引脚特殊功能引脚彩色具有特定功能无法通过编程控制注电源正极——VDD电源负极——VSS普通IO引脚命名GPIOA:PA0,PA1...PA15 GPIOB:PB0,PB1...PB15 GPIOC:PC13,PC14,PC15 GPIOD:PD0,PD1IO复用与复用功能重映射通用功能通过编程直接控制引脚复用功能为芯片上其他模块使用IO复用——同一个IO引脚具有不同的功能通用功能/复用功能复用功能重映射——将冲突的复用功能移动到备用引脚上GPIO的8种工作模式IO引脚连通芯片的内部和外部输出高/低电压 ||测外部高/低电压输入向 STM32 输入外部给 STM32信号输出STM32 输出STM32 给外部信号输入与输出的本质区别在于信号的流向。输入是外部世界向芯片传递信息的过程例如按键按下时产生的电平变化、传感器输出的模拟或数字信号都需要通过输入引脚进入 STM32 内部由 CPU 或外设读取并处理。输出则是 STM32 将内部运算结果或控制指令传递给外部设备的过程例如点亮 LED、驱动蜂鸣器、控制电机转速等都是通过输出引脚向外发送高电平或低电平信号。理解输入与输出的方向性是正确配置 GPIO 模式的前提。如果方向搞反例如把本该配置为输入的引脚误设为输出轻则功能异常重则可能因电平冲突损坏引脚。因此在实际开发中务必先明确信号流向再选择对应的工作模式。输入向 STM32 输入外部给 STM32信号输出STM32 输出STM32 给外部信号输入与输出的本质区别在于信号的流向。输入是外部世界向芯片传递信息的过程例如按键按下时产生的电平变化、传感器输出的模拟或数字信号都需要通过输入引脚进入 STM32 内部由 CPU 或外设读取并处理。输出则是 STM32 将内部运算结果或控制指令传递给外部设备的过程例如点亮 LED、驱动蜂鸣器、控制电机转速等都是通过输出引脚向外发送高电平或低电平信号。理解输入与输出的方向性是正确配置 GPIO 模式的前提。如果方向搞反例如把本该配置为输入的引脚误设为输出轻则功能异常重则可能因电平冲突损坏引脚。因此在实际开发中务必先明确信号流向再选择对应的工作模式。4种输出模式推挽——交替导通P关N开 / P开N关开漏——上管( P )恒断推挽输出向IO写0——低电压向IO写1——高电压开漏输出向IO写0——低电压向IO写1——高阻抗通用输出推挽通用输出开漏复用输出推挽复用输出开漏4种输入模式输入上拉在引脚悬空时提供高电压输入下拉在引脚悬空时提供低电压输入上拉输入下拉输入浮空模拟模式GPIO两个实验单片机驱动LED的两种方式推挽接法接地——写0熄灭写1点亮开漏接法接VDD——写1熄灭写0点亮STM32F103C8T6上板载LED为开漏接法PC13引脚CubeMX的使用新建工程后首先设置芯片的调试接口参数设置LED闪烁/* USER CODE BEGIN WHILE */while(1){HAL_GPIO_WritePin(GPIOA,GPIO_PIN_9,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);HAL_Delay(500);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_9,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);HAL_Delay(500);/* USER CODE END WHILE */按键控制LED/* USER CODE BEGIN WHILE */while(1){if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_9)1){HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_10,GPIO_PIN_SET);}else{HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_10,GPIO_PIN_RESET);}/* USER CODE END WHILE */*注代码写在USER CODE BEGIN 和 USER CODE END 之间SET——1RESET——0UART串口串口是什么————一种通讯接口传输数据需要有一定的格式串口以“数据帧”为单位传输数据串口数据帧的组成校验位的方法奇校验保证数据位包含奇数个1偶校验保证数据位包含偶数个1波特率每秒钟传输位的数量UART两个实验一.串口发送数据首先配置调试接口为串口选择工作模式TX引脚配置为复用推挽输出RX引脚配置为输入浮空或者上拉配置参数保证配置参数的一致单片机编程的数据类型串口发送的函数/* USER CODE BEGIN 2 */uint8_tnumber0x5a;uint8_tch[]{1,2,3,4,5};charchha;char*strHello;HAL_UART_Transmit(huart1,number,1,HAL_MAX_DELAY);HAL_UART_Transmit(huart1,ch,5,HAL_MAX_DELAY);HAL_UART_Transmit(huart1,(uint8_t*)chh,1,HAL_MAX_DELAY);HAL_UART_Transmit(huart1,(uint8_t*)str,strlen(str),HAL_MAX_DELAY);/* USER CODE END 2 */二.串口接收数据while(1){/* USER CODE END WHILE */uint8_treceive;HAL_UART_Receive(huart1,receive,1,HAL_MAX_DELAY);if(receive1){HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);}elseif(receive0){HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);}/* USER CODE BEGIN 3 */}I2C总线多个设备共享一组信号线并按照规则通信I2C有SCL,SDA两条信号线其中SCLSDA模式为开漏输出且SCLSDA都接上拉电阻原因为了实现逻辑线与I2C通过逻辑线与输出0/1.I2C的通信过程起始位——寻址——数据传输——停止位主机拉低SDA起始位——主机发送寻址位并选择输出从机应答寻址——主机发送数据从机应答数据传输——主机拉高SDA停止位主机拉低SDA起始位——主机发送寻址位并选择输入从机应答寻址——从机发送数据主机应答数据传输——主机拉高SDA停止位**I2C通过寻址的最后一位来实现输出/接受0输出单片机主机发送数据1I2C实验点亮屏幕读取屏幕状态屏幕初始化命令/* USER CODE BEGIN 2 */uint8_tcommands[]{0x00,0x8d,0x14,0xaf,0xa5};//屏幕初始化命令数组HAL_I2C_Master_Transmit(hi2c1,0x78,commands,sizeof(commands)/sizeof(commands[0]),HAL_MAX_DELAY);uint8_treceive;HAL_I2C_Master_Receive(hi2c1,0x78,receive,1,HAL_MAX_DELAY);if((receive(0x016))0){HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);}else{HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);}/* USER CODE END 2 */时钟系统单片机与电脑的类比理解为什么要有时钟因为时序逻辑电路有时钟信号才能工作而单片机的模块都是时序电路。时钟是干嘛的时钟决定了电路运行速度的快慢时钟为单片机各个部分提供一种“节奏”“节拍”。时钟的产生与分配时钟的产生高速内部时钟HSI;高速外部时钟HSE;低速内部时钟LSI低速外部时钟LSE内部表示时钟源在芯片内——内部的RC振荡器产生时钟精度低外部表示时钟源需外接——外部晶振产生时钟成本高时钟的分配几个重要的关键节点SYSCLK——有HSI,HSE,PLL锁相环分频器三种来源——最高72MHCLK——SYSCLK经过AHB分频器产生——最高72MPCLK1——HCLK 经过APB1分频器产生为低速外设提供时钟最高 36MPCLK2——HCLK 经过APB2分频器产生为高速外设提供时钟最高 72M时钟系统配置实验对应理解stm32内核Cortex—M3的频率来自HCLK配置时钟树的参数改变时钟频率cortex-m3的观察LED闪烁速度的变化/* USER CODE BEGIN WHILE */while(1){uint32_ti;for(i0;i1000000;i){}HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);for(i0;i1000000;i){}HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);/* USER CODE END WHILE */SPI使用场景高速双向M:Master(主机S:Slave(从机NSS: Negative Slave Select 低电压从机选择MOSI:主机发送从机接收MISO:主机接收从机发送SCK串行时钟线由主机发送给从机NSS主机发送低电压选择从机*****SPI的五个参数*****1.波特率波特率每秒传输的高/低电压的数量波特率越大传输速度越快选取波特率原则1.选择允许的最大值2.考虑设备承受极限3.考虑电路板承受极限2.比特率传输顺序LSB 最低有效位先传低位MSB最高有效位先传高位3.数据位长度4.时钟极性低极性空闲状态为低电压高极性空闲状态为高电压第一边沿第一个出现的边沿第二边沿第二个出现的边沿如果为低极性则第一边沿为上升沿如果为高极性则第一边沿为下降沿5.时钟相位时钟的极性相位——四种模式SPI实验flash模块的引脚命名flash模块引脚是以从机的角度来命名因此略有不同SPI配置参数选择两个函数注接收数据函数的接收缓冲区赋0xff;写入flash读取flash过程写使能擦除扇区页编程读取flash写入/读取flash函数/* USER CODE BEGIN PFP */staticvoidSaveLED_State(uint8_tLED);staticuint8_tRead_State(void);/* USER CODE END PFP *//* Private user code ---------------------------------------------------------*//* USER CODE BEGIN 0 */staticvoidSaveLED_State(uint8_tLED){//1.写使能uint8_tWrite_Enable[]{0x06};HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);HAL_SPI_Transmit(hspi1,Write_Enable,1,HAL_MAX_DELAY);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);//2.擦除扇区uint8_tErase[]{0x20,0x00,0x00,0x00};HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);HAL_SPI_Transmit(hspi1,Erase,4,HAL_MAX_DELAY);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);HAL_Delay(100);//延时//3.写使能HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);HAL_SPI_Transmit(hspi1,Write_Enable,1,HAL_MAX_DELAY);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);//4.页编程uint8_tPage_Write[]{0x02,0x00,0x00,0x00,LED};HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);HAL_SPI_Transmit(hspi1,Page_Write,5,HAL_MAX_DELAY);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);HAL_Delay(10);//延时}staticuint8_tRead_State(void){uint8_tread_cmd[]{0x03,0x00,0x00,0x00};uint8_tled_state0xff;HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_RESET);HAL_SPI_Transmit(hspi1,read_cmd,4,HAL_MAX_DELAY);HAL_SPI_Receive(hspi1,led_state,1,HAL_MAX_DELAY);HAL_GPIO_WritePin(GPIOA,GPIO_PIN_4,GPIO_PIN_SET);returnled_state;}/* USER CODE END 0 */按键控制LED松手触发/* USER CODE BEGIN WHILE */uint8_tpre1,cur1;uint8_tLED0;LEDRead_State();LEDRead_State();if(LED0){HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);}else{HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);}while(1){precur;if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)GPIO_PIN_SET){cur1;}else{cur0;}if(pre!cur){HAL_Delay(10);if(cur0){}else{if(LED1){HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);LED0;}else{HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);LED1;}SaveLED_State(LED);}}/* USER CODE END WHILE */中断中断优先级分组抢占优先级中断嵌套中断排队子优先级中断排队∴先比较抢占优先级然后比较子优先级本质上是把一个数拆成两个高位给抢占优先级低位给子优先级NVIC模块作用1.使能和禁止中断哪个中断能发生2.中断优先级分组抢占优先级几个子优先级几个)3.仲裁电路决定中断排队和中断嵌套∴NVIC就是中断的管理员中断实验实验思路串口发送数据控制不同的led闪频中断相应函数与回调函数配合实现/* USER CODE BEGIN PV */staticuint32_tled_delay1000;staticuint8_tdata;/* USER CODE END PV *//* USER CODE BEGIN 0 */voidHAL_UART_RxCpltCallback(UART_HandleTypeDef*huart)//回调函数{if(huarthuart1){if(data1){led_delay1000;}elseif(data2){led_delay500;}elseif(data3){led_delay100;}HAL_UART_Receive_IT(huart1,data,1);}}/* USER CODE END 0 *//* USER CODE BEGIN WHILE */HAL_UART_Receive_IT(huart1,data,1);while(1){HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);HAL_Delay(led_delay);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);HAL_Delay(led_delay);/* USER CODE END WHILE */定时器定时器的内部结构时基单元从模式控制器输入捕获输出比较一.时基单元首先时钟树为定时器分配时钟频率1.预分频器PSC——降频/PSC12.计数器CNT——上计数1/下计数-1/中心对齐3.自动重装寄存器ARR——设置定时周期发生溢出的值4.重复计数器RCR——产生RCR1次溢出发生一次update事件定时器种类补充寄存器预加载——缓冲机制更安全防跑飞PSC,RCR是强制开启的ARR需手动开启自制延时函数实验/* USER CODE BEGIN 0 */staticvoidmy_delay(uint32_tDELAY){uint32_texpire_timeget_time()DELAY;while(get_time()expire_time);}staticuint32_tget_time(void){returncurrent_time;}voidHAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef*htim){if(htimhtim1){current_time;}}/* USER CODE END 0 *//* USER CODE BEGIN WHILE */HAL_TIM_Base_Start_IT(htim1);while(1){HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);my_delay(1000);HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);my_delay(1000);/* USER CODE END WHILE */二.输出比较常用于输出PWM波形时基单元决定周期CCR决定占空比①将CCR与CNT进行比较模式选择①什么情况输出高电压/什么情况输出低电压极性选择正极性positive信号原样输出负极性negative信号取反输出CH1正常输出CH1N:互补输出模式选择8种PWM呼吸灯/* USER CODE BEGIN WHILE */HAL_TIM_PWM_Start(htim1,TIM_CHANNEL_1);HAL_TIMEx_PWMN_Start(htim1,TIM_CHANNEL_1);while(1){floattHAL_GetTick()*0.0008;floatduty0.5*sin(2*3.14*t)0.5;uint16_tarr__HAL_TIM_GET_AUTORELOAD(htim1);uint16_tccrduty*(arr1);__HAL_TIM_SET_COMPARE(htim1,TIM_CHANNEL_1,ccr);/* USER CODE END WHILE */三.输入捕获输入捕获捕捉信号变化的时间点并保存保存到ccr)参数设置将检测到的边沿时刻相减ccr2-ccr1),通过不同运算可计算脉冲宽度距离等。读取寄存器的一些函数输入捕获测距离让计数器CNT清零——清除CC1/CC2标志位——启动输入捕获——向Trig发送脉冲——等待测量结束——关闭输入捕获——计算测量结果四.从模式控制器模式复位模式 测占空比周期实验一/* USER CODE BEGIN WHILE */HAL_TIM_PWM_Start(htim3,TIM_CHANNEL_1);while(1){//1.清除cc1标志位__HAL_TIM_CLEAR_FLAG(htim1,TIM_FLAG_CC1);//2.启动定时器HAL_TIM_IC_Start(htim1,TIM_CHANNEL_1);HAL_TIM_IC_Start(htim1,TIM_CHANNEL_2);//3.等待cc1标志位并再次清除while(__HAL_TIM_GET_FLAG(htim1,TIM_FLAG_CC1)0);__HAL_TIM_CLEAR_FLAG(htim1,TIM_FLAG_CC1);//4.再次等待cc1标志位while(__HAL_TIM_GET_FLAG(htim1,TIM_FLAG_CC1)0);//5.关闭定时器HAL_TIM_IC_Stop(htim1,TIM_CHANNEL_1);HAL_TIM_IC_Stop(htim1,TIM_CHANNEL_2);//6.计算测量结果uint16_tcc1__HAL_TIM_GET_COMPARE(htim1,TIM_CHANNEL_1);uint16_tcc2__HAL_TIM_GET_COMPARE(htim1,TIM_CHANNEL_2);floatperiodcc1*1e-6f;floatpluswidthcc2*1e-6f;floatdutypluswidth/period;UART_Printf(脉宽 %.1fus,周期 %.1fus,占空比 %.1f%%,pluswidth*1e6f,period*1e6f,duty*100.0f);HAL_Delay(500);/* USER CODE END WHILE */编码器模式控制LED实验二编码器工作原理A相在前表示正转计数器CNT递增B相在前表示反转计数器CNT递减/* USER CODE BEGIN 2 */HAL_TIM_PWM_Start(htim1,TIM_CHANNEL_1);HAL_TIM_Encoder_Start(htim3,TIM_CHANNEL_1);HAL_TIM_Encoder_Start(htim3,TIM_CHANNEL_2);/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while(1){uint16_tcnt__HAL_TIM_GET_COUNTER(htim3);uint16_tccrcnt;__HAL_TIM_SET_COMPARE(htim1,TIM_CHANNEL_1,ccr);/* USER CODE END WHILE */