STM32 HAL库外设编程学习笔记

STM32 HAL库外设编程

1. 概述

本文档是基于STM32 HAL库的外设编程学习笔记,主要包括以下外设的配置和使用方法:

  • GPIO:通用输入输出接口
  • ADC:模数转换器
  • UART:通用异步收发器
  • TIM:定时器
  • I2C:内部集成电路总线
  • SPI:串行外设接口

本笔记基于STM32F1系列微控制器,使用HAL库进行开发。

2. GPIO (通用输入输出接口)

2.1 GPIO初始化

c 复制代码
void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOD_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);

  /*Configure GPIO pin : PC13 - LED */
  GPIO_InitStruct.Pin = GPIO_PIN_13;
  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);

  /*Configure GPIO pin : PA0 - Button */
  GPIO_InitStruct.Pin = GPIO_PIN_0;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;     // 输入模式
  GPIO_InitStruct.Pull = GPIO_PULLUP;         // 上拉
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

2.2 GPIO操作函数

c 复制代码
// LED控制函数
void led_ON(void)
{
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET); // 低电平点亮
}

void led_OFF(void)
{
    HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_SET);  // 高电平熄灭
}

void led_Tog(void)
{
    HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13);  // 翻转LED状态
}

// 按钮读取函数(带消抖)
uint8_t read_button(void)
{
    if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET){
        HAL_Delay(10);  // 消抖延时
        while(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0) == GPIO_PIN_RESET); // 等待按钮释放
        HAL_Delay(10);  // 释放消抖
        return 1;
    }
    return 0;
}

2.3 GPIO常用API

  • HAL_GPIO_Init(): 初始化GPIO引脚
  • HAL_GPIO_DeInit(): 复位GPIO引脚
  • HAL_GPIO_ReadPin(): 读取GPIO引脚状态
  • HAL_GPIO_WritePin(): 设置GPIO引脚状态
  • HAL_GPIO_TogglePin(): 翻转GPIO引脚状态
  • HAL_GPIO_LockPin(): 锁定GPIO引脚配置

3. ADC (模数转换器)


3.1 ADC初始化

c 复制代码
void MX_ADC1_Init(void)
{
  ADC_ChannelConfTypeDef sConfig = {0};

  /* ADC1基本配置 */
  hadc1.Instance = ADC1;
  hadc1.Init.ScanConvMode = ADC_SCAN_DISABLE;           // 禁用扫描模式
  hadc1.Init.ContinuousConvMode = DISABLE;              // 禁用连续转换
  hadc1.Init.DiscontinuousConvMode = DISABLE;           // 禁用不连续模式
  hadc1.Init.ExternalTrigConv = ADC_EXTERNALTRIGCONV_T3_TRGO; // 外部触发源为TIM3
  hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;          // 数据右对齐
  hadc1.Init.NbrOfConversion = 1;                       // 转换通道数为1
  HAL_ADC_Init(&hadc1);

  /* ADC通道配置 */
  sConfig.Channel = ADC_CHANNEL_1;                      // 选择通道1
  sConfig.Rank = ADC_REGULAR_RANK_1;                    // 设置为第1个转换
  sConfig.SamplingTime = ADC_SAMPLETIME_13CYCLES_5;     // 采样时间为13.5个周期
  HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}

3.2 ADC引脚配置

c 复制代码
void HAL_ADC_MspInit(ADC_HandleTypeDef* adcHandle)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(adcHandle->Instance==ADC1)
  {
    /* ADC1时钟使能 */
    __HAL_RCC_ADC1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    /* ADC1 GPIO配置: PA1 -> ADC1_IN1 */
    GPIO_InitStruct.Pin = GPIO_PIN_1;
    GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;  // 模拟输入模式
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
  }
}

3.3 ADC使用示例

c 复制代码
// 获取ADC值并转换为电压值
float get_adc_value(void)
{
    HAL_ADC_Start(&hadc1);  // 启动ADC转换
    HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);  // 等待转换完成
    uint32_t dr = HAL_ADC_GetValue(&hadc1);  // 获取转换结果
    return dr * ((3.3f - 0.0f) / 4095.0f);  // 转换为电压值(0-3.3V)
}

// 使用定时器触发的ADC转换
float get_adc_value_print(void)
{
    HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);  // 等待转换完成
    uint32_t dr = HAL_ADC_GetValue(&hadc1);  // 获取转换结果
    return dr * ((3.3f - 0.0f) / 4095.0f);  // 转换为电压值(0-3.3V)
}

3.4 ADC常用API

  • HAL_ADC_Init(): 初始化ADC
  • HAL_ADC_ConfigChannel(): 配置ADC通道
  • HAL_ADC_Start(): 启动ADC转换
  • HAL_ADC_Stop(): 停止ADC转换
  • HAL_ADC_PollForConversion(): 轮询等待转换完成
  • HAL_ADC_GetValue(): 获取转换结果
  • HAL_ADC_Start_IT(): 启动中断模式ADC转换
  • HAL_ADC_Start_DMA(): 启动DMA模式ADC转换

4. UART (通用异步收发器)


4.1 UART初始化

c 复制代码
void MX_USART1_UART_Init(void)
{
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;                  // 波特率115200
  huart1.Init.WordLength = UART_WORDLENGTH_8B;    // 8位数据位
  huart1.Init.StopBits = UART_STOPBITS_1;         // 1位停止位
  huart1.Init.Parity = UART_PARITY_NONE;          // 无校验位
  huart1.Init.Mode = UART_MODE_TX_RX;             // 收发模式
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;    // 无硬件流控
  huart1.Init.OverSampling = UART_OVERSAMPLING_16; // 16倍过采样
  HAL_UART_Init(&huart1);
}

4.2 UART引脚配置

c 复制代码
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {
    /* USART1时钟使能 */
    __HAL_RCC_USART1_CLK_ENABLE();
    __HAL_RCC_GPIOA_CLK_ENABLE();
    
    /* USART1 GPIO配置
    PA9     ------> USART1_TX
    PA10    ------> USART1_RX */
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;       // 复用推挽输出
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;        // 输入模式
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* USART1中断配置 */
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);
  }
}

4.3 UART中断接收实现

c 复制代码
// 全局变量
uint16_t UART1_INDEX = 0;         // 缓冲区索引
uint8_t UART1_STATE = 0;           // 状态机状态
uint8_t UART1_BUFFER[256];         // 接收缓冲区
uint8_t UART1_TEMP_CHAR;           // 临时接收字符

// 启动UART1中断接收
void uart1_start_it(void)
{
    HAL_UART_Receive_IT(&huart1, &UART1_TEMP_CHAR, 1); // 启动接收中断
}

// UART接收完成回调函数
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
    if (huart == &huart1)
    {
        // 状态机处理
        switch (UART1_STATE)
        {
        case 0:  // 正常接收状态
            if (UART1_TEMP_CHAR == '\r')  // 接收到回车
            {
                UART1_STATE = 1;  // 进入等待换行状态
            }
            else
            {
                if (UART1_INDEX < UART1_BUFFER_SIZE - 1)
                {
                    UART1_BUFFER[UART1_INDEX++] = UART1_TEMP_CHAR;  // 存储字符
                }
            }
            break;

        case 1:  // 等待换行状态
            if (UART1_TEMP_CHAR == '\n')  // 接收到换行
            {
                UART1_BUFFER[UART1_INDEX] = '\0';  // 字符串结束符
                UART1_STATE = 2;  // 进入数据就绪状态
            }
            else
            {
                // 不是换行,补存\r和当前字符
                if (UART1_INDEX < UART1_BUFFER_SIZE - 2)
                {
                    UART1_BUFFER[UART1_INDEX++] = '\r';
                    UART1_BUFFER[UART1_INDEX++] = UART1_TEMP_CHAR;
                }
                UART1_STATE = 0;  // 回到正常接收状态
            }
            break;
        }

        // 缓冲区溢出处理
        if (UART1_INDEX >= UART1_BUFFER_SIZE - 1)
        {
            UART1_INDEX = 0;                            // 重置索引
            UART1_STATE = 0;                             // 重置状态
            memset(UART1_BUFFER, 0, UART1_BUFFER_SIZE);  // 清空缓冲区
        }

        // 重启接收中断
        HAL_UART_Receive_IT(&huart1, &UART1_TEMP_CHAR, 1);
    }
}

4.4 printf重定向实现

c 复制代码
// 重定向printf到UART2
int fputc(int ch, FILE *f)
{
    if (f == stdout)  // 仅处理标准输出
    {
        HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 1, 100);  // 阻塞发送
        if (ch == '\n')  // 发送\n时自动补充\r
            HAL_UART_Transmit(&huart2, (uint8_t *)"\r", 1, 100);
    }
    return ch;
}

// 调试日志函数
void DEBUG_LOG(char *file, char *info)
{
    printf("Time: %d, File: %s, Info: %s\r\n", CURRENT_TIME, file, info);
}

4.5 UART常用API

  • HAL_UART_Init(): 初始化UART
  • HAL_UART_Transmit(): 阻塞模式发送数据
  • HAL_UART_Receive(): 阻塞模式接收数据
  • HAL_UART_Transmit_IT(): 中断模式发送数据
  • HAL_UART_Receive_IT(): 中断模式接收数据
  • HAL_UART_Transmit_DMA(): DMA模式发送数据
  • HAL_UART_Receive_DMA(): DMA模式接收数据

5. TIM (定时器)

5.1 基本定时器初始化

c 复制代码
void MX_TIM1_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  /* TIM1基本配置 */
  htim1.Instance = TIM1;
  htim1.Init.Prescaler = 7200-1;                    // 预分频值,72MHz/7200=10KHz
  htim1.Init.CounterMode = TIM_COUNTERMODE_UP;      // 向上计数模式
  htim1.Init.Period = 10000;                        // 周期值,10KHz/10000=1Hz
  htim1.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // 时钟分频因子
  htim1.Init.RepetitionCounter = 0;                 // 重复计数器值
  htim1.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; // 禁用自动重装载预装载
  HAL_TIM_Base_Init(&htim1);

  /* 时钟源配置 */
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL; // 内部时钟源
  HAL_TIM_ConfigClockSource(&htim1, &sClockSourceConfig);

  /* 主模式配置 */
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; // 复位触发输出
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; // 禁用主从模式
  HAL_TIMEx_MasterConfigSynchronization(&htim1, &sMasterConfig);
}

5.2 PWM定时器初始化

c 复制代码
void MX_TIM3_Init(void)
{
  TIM_MasterConfigTypeDef sMasterConfig = {0};
  TIM_OC_InitTypeDef sConfigOC = {0};

  /* TIM3基本配置 */
  htim3.Instance = TIM3;
  htim3.Init.Prescaler = 72-1;                     // 预分频值,72MHz/72=1MHz
  htim3.Init.CounterMode = TIM_COUNTERMODE_UP;     // 向上计数模式
  htim3.Init.Period = 1000;                        // 周期值,1MHz/1000=1KHz
  htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; // 时钟分频因子
  htim3.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; // 禁用自动重装载预装载
  HAL_TIM_PWM_Init(&htim3);

  /* 主模式配置 */
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE; // 更新事件触发输出
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; // 禁用主从模式
  HAL_TIMEx_MasterConfigSynchronization(&htim3, &sMasterConfig);

  /* PWM通道配置 */
  sConfigOC.OCMode = TIM_OCMODE_PWM1;             // PWM模式1
  sConfigOC.Pulse = 0;                             // 初始占空比为0
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;      // 输出极性为高
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;       // 禁用快速模式
  HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, TIM_CHANNEL_3); // 配置通道3为PWM
}

5.3 定时器中断处理

c 复制代码
// 全局变量
uint32_t CURRENT_TIME;  // 当前时间计数

// 定时器周期中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
    if (htim->Instance == TIM1){
      CURRENT_TIME++;  // 时间计数递增
      led_Tog();       // 翻转LED状态
    }
}

// 启动定时器中断
void tim_start(void)
{
    HAL_TIM_Base_Start_IT(&htim1);  // 启动基本定时器中断
    DEBUG_LOG("tim", "TIM1 started");
}

5.4 PWM控制函数

c 复制代码
// 启动PWM输出
void pwm_start(void)
{
    HAL_TIM_PWM_Start(&htim3, TIM_CHANNEL_3);  // 启动PWM输出
}

// 更新PWM占空比
void pwm_update(int duty)
{
    __HAL_TIM_SET_COMPARE(&htim3, TIM_CHANNEL_3, duty);  // 设置比较值
}

5.5 定时器常用API

  • HAL_TIM_Base_Init(): 初始化基本定时器
  • HAL_TIM_PWM_Init(): 初始化PWM定时器
  • HAL_TIM_Base_Start(): 启动基本定时器
  • HAL_TIM_Base_Stop(): 停止基本定时器
  • HAL_TIM_Base_Start_IT(): 启动基本定时器中断
  • HAL_TIM_PWM_Start(): 启动PWM输出
  • HAL_TIM_PWM_Stop(): 停止PWM输出
  • HAL_TIM_PWM_ConfigChannel(): 配置PWM通道

6. I2C (内部集成电路总线)

6.1 I2C初始化

c 复制代码
void MX_I2C1_Init(void)
{
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 400000;                // 时钟速度400KHz
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;        // 占空比2:1
  hi2c1.Init.OwnAddress1 = 0;                    // 自身地址1
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; // 7位地址模式
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; // 禁用双地址模式
  hi2c1.Init.OwnAddress2 = 0;                    // 自身地址2
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; // 禁用广播呼叫模式
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; // 禁用时钟延展模式
  HAL_I2C_Init(&hi2c1);
}

6.2 I2C OLED显示屏操作

c 复制代码
// OLED写命令
void OLED_WriteCommand(uint8_t Command)
{
    uint8_t cmd[] = {0x00, Command};  // 0x00表示命令
    HAL_I2C_Master_Transmit(&hi2c1, 0x78, cmd, sizeof(cmd), HAL_MAX_DELAY);
}

// OLED写数据
void OLED_WriteData(uint8_t Data)
{
    uint8_t cmd[] = {0x40, Data};     // 0x40表示数据
    HAL_I2C_Master_Transmit(&hi2c1, 0x78, cmd, sizeof(cmd), HAL_MAX_DELAY);
}

// OLED设置光标位置
void OLED_SetCursor(uint8_t Y, uint8_t X)
{
    OLED_WriteCommand(0xB0 | Y);                    // 设置Y位置
    OLED_WriteCommand(0x10 | ((X & 0xF0) >> 4));    // 设置X位置高4位
    OLED_WriteCommand(0x00 | (X & 0x0F));           // 设置X位置低4位
}

// OLED清屏
void OLED_Clear(void)
{  
    uint8_t i, j;
    for (j = 0; j < 8; j++)
    {
        OLED_SetCursor(j, 0);
        for(i = 0; i < 128; i++)
        {
            OLED_WriteData(0x00);
        }
    }
}

// OLED显示字符
void OLED_ShowChar(uint8_t Line, uint8_t Column, char Char)
{      
    uint8_t i;
    OLED_SetCursor((Line - 1) * 2, (Column - 1) * 8);        // 设置光标位置在上半部分
    for (i = 0; i < 8; i++)
    {
        OLED_WriteData(OLED_F8x16[Char - ' '][i]);           // 显示上半部分内容
    }
    OLED_SetCursor((Line - 1) * 2 + 1, (Column - 1) * 8);    // 设置光标位置在下半部分
    for (i = 0; i < 8; i++)
    {
        OLED_WriteData(OLED_F8x16[Char - ' '][i + 8]);       // 显示下半部分内容
    }
}

// OLED显示字符串
void OLED_ShowString(uint8_t Line, uint8_t Column, char *String)
{
    uint8_t i;
    for (i = 0; String[i] != '\0'; i++)
    {
        OLED_ShowChar(Line, Column + i, String[i]);
    }
}

// OLED显示数字
void OLED_ShowNum(uint8_t Line, uint8_t Column, uint32_t Number, uint8_t Length)
{
    uint8_t i;
    for (i = 0; i < Length; i++)                            
    {
        OLED_ShowChar(Line, Column + i, Number / OLED_Pow(10, Length - i - 1) % 10 + '0');
    }
}

// OLED初始化
void OLED_start(void)
{
    // 上电延时
    uint32_t i, j;
    for (i = 0; i < 1000; i++)
    {
        for (j = 0; j < 1000; j++);
    }
    
    // 初始化命令序列
    OLED_WriteCommand(0xAE);    // 关闭显示
    OLED_WriteCommand(0xD5);    // 设置显示时钟分频比/振荡器频率
    OLED_WriteCommand(0x80);
    OLED_WriteCommand(0xA8);    // 设置多路复用率
    OLED_WriteCommand(0x3F);
    OLED_WriteCommand(0xD3);    // 设置显示偏移
    OLED_WriteCommand(0x00);
    OLED_WriteCommand(0x40);    // 设置显示开始行
    OLED_WriteCommand(0xA1);    // 设置左右方向,0xA1正常 0xA0左右反置
    OLED_WriteCommand(0xC8);    // 设置上下方向,0xC8正常 0xC0上下反置
    OLED_WriteCommand(0xDA);    // 设置COM引脚硬件配置
    OLED_WriteCommand(0x12);
    OLED_WriteCommand(0x81);    // 设置对比度控制
    OLED_WriteCommand(0xCF);
    OLED_WriteCommand(0xD9);    // 设置预充电周期
    OLED_WriteCommand(0xF1);
    OLED_WriteCommand(0xDB);    // 设置VCOMH取消选择级别
    OLED_WriteCommand(0x30);
    OLED_WriteCommand(0xA4);    // 设置整个显示打开/关闭
    OLED_WriteCommand(0xA6);    // 设置正常/倒转显示
    OLED_WriteCommand(0x8D);    // 设置充电泵
    OLED_WriteCommand(0x14);
    OLED_WriteCommand(0xAF);    // 开启显示
    
    OLED_Clear();               // OLED清屏
}

6.3 I2C常用API

  • HAL_I2C_Init(): 初始化I2C
  • HAL_I2C_Master_Transmit(): 主机模式发送数据
  • HAL_I2C_Master_Receive(): 主机模式接收数据
  • HAL_I2C_Slave_Transmit(): 从机模式发送数据
  • HAL_I2C_Slave_Receive(): 从机模式接收数据
  • HAL_I2C_Mem_Write(): 写入设备内部地址
  • HAL_I2C_Mem_Read(): 读取设备内部地址

7. SPI (串行外设接口)

7.1 SPI初始化

c 复制代码
void MX_SPI1_Init(void)
{
  hspi1.Instance = SPI1;
  hspi1.Init.Mode = SPI_MODE_MASTER;                // 主机模式
  hspi1.Init.Direction = SPI_DIRECTION_2LINES;      // 双线全双工
  hspi1.Init.DataSize = SPI_DATASIZE_8BIT;          // 8位数据位
  hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;       // 时钟极性高
  hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;            // 第二个边沿采样
  hspi1.Init.NSS = SPI_NSS_SOFT;                    // 软件NSS控制
  hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8; // 波特率预分频
  hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;           // 高位先行
  hspi1.Init.TIMode = SPI_TIMODE_DISABLE;           // 禁用TI模式
  hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; // 禁用CRC计算
  hspi1.Init.CRCPolynomial = 10;                    // CRC多项式
  HAL_SPI_Init(&hspi1);
}

7.2 SPI Flash操作示例

c 复制代码
// 保存LED状态到Flash
void save_led_state(uint8_t led_state)
{
    // 1. 写使能
    uint8_t write_cmd[] = {0x06};
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);  // CS拉低
    HAL_SPI_Transmit(&hspi1, write_cmd, sizeof(write_cmd), HAL_MAX_DELAY);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);    // CS拉高
    
    // 2. 扇区擦除
    uint8_t sector_erase[] = {0x20, 0x00, 0x00, 0x00};    // 擦除0地址扇区
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi1, sector_erase, sizeof(sector_erase), HAL_MAX_DELAY);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
    HAL_Delay(200);  // 等待擦除完成
    
    // 3. 写使能
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi1, write_cmd, sizeof(write_cmd), HAL_MAX_DELAY);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
    
    // 4. 页编程
    uint8_t page_cmd[] = {0x02, 0x00, 0x00, 0x00, led_state};  // 写入数据
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);
    HAL_SPI_Transmit(&hspi1, page_cmd, sizeof(page_cmd), HAL_MAX_DELAY);
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);
    HAL_Delay(100);  // 等待写入完成
}

// 从Flash读取LED状态
uint8_t load_led_state(void)
{
    uint8_t read_cmd[] = {0x03, 0x00, 0x00, 0x00};  // 读取命令
    uint8_t led_state = 0xff;
    
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET);  // CS拉低
    HAL_SPI_Transmit(&hspi1, read_cmd, sizeof(read_cmd), HAL_MAX_DELAY);
    HAL_SPI_Receive(&hspi1, &led_state, 1, HAL_MAX_DELAY);  // 接收数据
    HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET);    // CS拉高
    
    return led_state;
}

7.3 SPI常用API

  • HAL_SPI_Init(): 初始化SPI
  • HAL_SPI_Transmit(): 发送数据
  • HAL_SPI_Receive(): 接收数据
  • HAL_SPI_TransmitReceive(): 同时发送和接收数据
  • HAL_SPI_Transmit_IT(): 中断模式发送数据
  • HAL_SPI_Receive_IT(): 中断模式接收数据
  • HAL_SPI_TransmitReceive_IT(): 中断模式同时发送和接收数据
  • HAL_SPI_Transmit_DMA(): DMA模式发送数据
  • HAL_SPI_Receive_DMA(): DMA模式接收数据

8. 综合应用示例

8.1 主函数初始化部分

c 复制代码
int main(void)
{
  /* 复位所有外设,初始化Flash接口和Systick */
  HAL_Init();

  /* 配置系统时钟 */
  SystemClock_Config();

  /* 初始化所有配置的外设 */
  MX_GPIO_Init();
  MX_TIM1_Init();
  MX_TIM3_Init();
  MX_USART1_UART_Init();
  MX_USART2_UART_Init();
  MX_SPI1_Init();
  MX_I2C1_Init();
  MX_ADC1_Init();
  
  /* 用户代码初始化 */
  HAL_UART_Transmit(&huart2, (uint8_t *)"hh", 2, 100);  // 测试UART发送
  tim_start();                // 启动定时器
  uart1_start_it();           // 启动UART1接收中断
  led_OFF();                  // 关闭LED
  OLED_start();               // 初始化OLED
  HAL_TIM_Base_Start(&htim3); // 启动TIM3基本定时器
  HAL_ADC_Start(&hadc1);      // 启动ADC
  
  /* 从Flash读取LED状态并设置 */
  uint8_t led_state = load_led_state();
  if (led_state == 1){
      led_ON();
  } else{
      led_OFF();
  }
  
  /* OLED显示初始信息 */
  OLED_ShowString(2, 2, "hello");
  OLED_ShowNum(1, 1, led_state, 1);
  
  /* 无限循环 */
  while (1)
  {
    /* ADC读取并打印 */
    float value = get_adc_value_print();
    printf("%.3f\n", value);
  }
}

8.2 条件编译示例

c 复制代码
// UART接收处理示例
#if 0
if (UART1_STATE == 2) // 检查是否接收到完整的字符串
{
    // 处理接收到的字符串
    printf("Received: %s\n", UART1_BUFFER); // 打印接收到的字符串
    UART1_INDEX = UART1_STATE = 0;
    memset(UART1_BUFFER, 0, sizeof(UART1_BUFFER));
}
#endif

// 按钮控制LED示例
#if 0
if (read_button() == 1)
{
    led_state = !led_state;
    switch_led(led_state);
    save_led_state(led_state);
}
#endif

// ADC控制LED示例
#if 0
float value = get_adc_value();
//OLED_ShowNum(1, 1, value, 5);
if (value < 1.5) led_ON();
else led_OFF();
#endif

9. 总结

本文档详细介绍了STM32 HAL库编程中常用外设的配置和使用方法,包括:

  1. GPIO:输入输出控制,LED和按钮操作
  2. ADC:模拟信号采集和转换
  3. UART:串口通信,中断接收和printf重定向
  4. TIM:基本定时器和PWM输出
  5. I2C:I2C通信,OLED显示屏控制
  6. SPI:SPI通信,Flash存储操作

通过这些基础外设的学习,可以进一步开发更复杂的STM32应用程序。在实际开发中,可以根据需要组合使用这些外设,实现各种功能。
Gitee 仓库地址

10. 参考资料

  1. STM32F1xx HAL库用户手册
  2. STM32F103数据手册
  3. STM32CubeMX用户指南