CSDN专栏:

第53讲:实战——标准GPIO量产驱动,Spec约束零BUG

一、GPIO驱动Spec定义

本讲通过完整Spec定义GPIO驱动,演示Spec驱动量产级代码生成。

1.1 GPIO驱动完整Spec

## Spec:GPIO量产驱动

### 1. 需求层(Requirement)

#### 1.1 功能需求
- REQ-001:配置GPIO引脚为输入/输出模式
- REQ-002:读取GPIO输入状态
- REQ-003:设置GPIO输出状态
- REQ-004:切换GPIO输出状态
- REQ-005:配置GPIO上下拉
- REQ-006:配置GPIO输出类型(推挽/开漏)
- REQ-007:配置GPIO速度

#### 1.2 性能需求
- REQ-010:GPIO操作响应时间<1us
- REQ-011:GPIO初始化时间<100us

#### 1.3 安全需求
- REQ-020:参数检查,防止非法端口
- REQ-021:参数检查,防止非法引脚
- REQ-022:参数检查,防止非法模式

### 2. 设计层(Design)

#### 2.1 硬件层约束
- DES-001:GPIO端口
  - GPIOA:PA0-PA15
  - GPIOB:PB0-PB15
  - GPIOC:PC0-PC15
  - GPIOD:PD0-PD15
  - GPIOE:PE0-PE15

- DES-002:GPIO模式
  - GPIO_MODE_INPUT:输入模式(0x00)
  - GPIO_MODE_OUTPUT_PP:推挽输出(0x01)
  - GPIO_MODE_OUTPUT_OD:开漏输出(0x02)
  - GPIO_MODE_AF_PP:复用推挽(0x03)
  - GPIO_MODE_AF_OD:复用开漏(0x04)
  - GPIO_MODE_ANALOG:模拟模式(0x05)

- DES-003:GPIO上下拉
  - GPIO_NOPULL:无上下拉(0x00)
  - GPIO_PULLUP:上拉(0x01)
  - GPIO_PULLDOWN:下拉(0x02)

- DES-004:GPIO速度
  - GPIO_SPEED_LOW:低速(0x00)
  - GPIO_SPEED_MEDIUM:中速(0x01)
  - GPIO_SPEED_HIGH:高速(0x02)
  - GPIO_SPEED_VERY_HIGH:超高速(0x03)

#### 2.2 数据层约束
- DES-010:GPIO端口类型
  - 类型:GPIO_TypeDef *
  - 有效值:GPIOA, GPIOB, GPIOC, GPIOD, GPIOE

- DES-011:GPIO引脚类型
  - 类型:uint16_t
  - 有效值:GPIO_PIN_0 ~ GPIO_PIN_15或GPIO_PIN_All

- DES-012:GPIO状态类型
  - 类型:uint8_t
  - 有效值:GPIO_PIN_RESET(0)或GPIO_PIN_SET(1)

#### 2.3 函数契约

##### 2.3.1 GPIO_Init
- DES-020:GPIO_Init
  - 签名:int8_t GPIO_Init(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_Config_t *Config)
  - 入参约束:
    - GPIOx非空且为有效端口
    - GPIO_Pin非零且为有效引脚
    - Config非空且配置有效
  - 返回值约束:
    - 0:成功
    - -1:参数错误
  - 执行时间上限:<100us

##### 2.3.2 GPIO_ReadPin
- DES-021:GPIO_ReadPin
  - 签名:int8_t GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, uint8_t *PinState)
  - 入参约束:
    - GPIOx非空且为有效端口
    - GPIO_Pin非零且为有效引脚
    - PinState非空
  - 出参约束:
    - PinState为GPIO_PIN_RESET或GPIO_PIN_SET
  - 返回值约束:
    - 0:成功
    - -1:参数错误
  - 执行时间上限:<1us

##### 2.3.3 GPIO_WritePin
- DES-022:GPIO_WritePin
  - 签名:int8_t GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, uint8_t PinState)
  - 入参约束:
    - GPIOx非空且为有效端口
    - GPIO_Pin非零且为有效引脚
    - PinState为GPIO_PIN_RESET或GPIO_PIN_SET
  - 返回值约束:
    - 0:成功
    - -1:参数错误
  - 执行时间上限:<1us

##### 2.3.4 GPIO_TogglePin
- DES-023:GPIO_TogglePin
  - 签名:int8_t GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
  - 入参约束:
    - GPIOx非空且为有效端口
    - GPIO_Pin非零且为有效引脚
  - 返回值约束:
    - 0:成功
    - -1:参数错误
  - 执行时间上限:<1us

#### 2.4 容错边界
- DES-030:非法端口
  - 检测方法:端口指针检查
  - 响应措施:返回-1

- DES-031:非法引脚
  - 检测方法:引脚值检查
  - 响应措施:返回-1

- DES-032:空指针
  - 检测方法:指针非空检查
  - 响应措施:返回-1

### 3. 编码层(Coding)

#### 3.1 编码规范
- COD-001:遵循MISRA C:2012规范
- COD-002:禁止动态内存分配
- COD-003:所有变量必须初始化
- COD-004:所有函数必须有单一出口

#### 3.2 命名规范
- COD-010:函数命名:GPIO_功能名
- COD-011:变量命名:类型前缀_模块_名称
- COD-012:宏定义:全大写

#### 3.3 注释规范
- COD-020:使用Doxygen格式注释
- COD-021:每个函数必须有注释块
- COD-022:注释必须标注对应Spec ID

### 4. 测试层(Test)

#### 4.1 单元测试
- TEST-001:测试GPIO初始化
  - 测试目标:DES-020
  - 测试输入:GPIOA, GPIO_PIN_5, 输出模式
  - 期望输出:返回0,PA5配置为输出

- TEST-002:测试GPIO写入
  - 测试目标:DES-022
  - 测试输入:GPIOA, GPIO_PIN_5, GPIO_PIN_SET
  - 期望输出:返回0,PA5输出高电平

- TEST-003:测试GPIO读取
  - 测试目标:DES-021
  - 测试输入:GPIOA, GPIO_PIN_5
  - 期望输出:返回0,正确读取PA5状态

- TEST-004:测试参数检查
  - 测试目标:DES-030, DES-031, DES-032
  - 测试输入:非法参数
  - 期望输出:返回-1

#### 4.2 集成测试
- TEST-010:测试GPIO输入输出
  - 测试场景:配置为输出,写入高电平,读取验证
  - 期望结果:读取值等于写入值

#### 4.3 覆盖率要求
- TEST-020:语句覆盖率:100%
- TEST-021:分支覆盖率:100%

二、GPIO驱动实现

2.1 头文件定义

/**
 * @file gpio.h
 * @brief GPIO驱动头文件
 * @note 对应Spec:GPIO量产驱动
 */

#ifndef __GPIO_H
#define __GPIO_H

#include "stm32f10x.h"
#include <stdint.h>

/* GPIO模式定义 - DES-002 */
#define GPIO_MODE_INPUT         (0x00U)
#define GPIO_MODE_OUTPUT_PP     (0x01U)
#define GPIO_MODE_OUTPUT_OD     (0x02U)
#define GPIO_MODE_AF_PP         (0x03U)
#define GPIO_MODE_AF_OD         (0x04U)
#define GPIO_MODE_ANALOG        (0x05U)

/* GPIO上下拉定义 - DES-003 */
#define GPIO_NOPULL             (0x00U)
#define GPIO_PULLUP             (0x01U)
#define GPIO_PULLDOWN           (0x02U)

/* GPIO速度定义 - DES-004 */
#define GPIO_SPEED_LOW          (0x00U)
#define GPIO_SPEED_MEDIUM       (0x01U)
#define GPIO_SPEED_HIGH         (0x02U)
#define GPIO_SPEED_VERY_HIGH    (0x03U)

/* GPIO引脚状态定义 - DES-012 */
#define GPIO_PIN_RESET          (0x00U)
#define GPIO_PIN_SET            (0x01U)

/* GPIO引脚定义 */
#define GPIO_PIN_0              (0x0001U)
#define GPIO_PIN_1              (0x0002U)
#define GPIO_PIN_2              (0x0004U)
#define GPIO_PIN_3              (0x0008U)
#define GPIO_PIN_4              (0x0010U)
#define GPIO_PIN_5              (0x0020U)
#define GPIO_PIN_6              (0x0040U)
#define GPIO_PIN_7              (0x0080U)
#define GPIO_PIN_8              (0x0100U)
#define GPIO_PIN_9              (0x0200U)
#define GPIO_PIN_10             (0x0400U)
#define GPIO_PIN_11             (0x0800U)
#define GPIO_PIN_12             (0x1000U)
#define GPIO_PIN_13             (0x2000U)
#define GPIO_PIN_14             (0x4000U)
#define GPIO_PIN_15             (0x8000U)
#define GPIO_PIN_All            (0xFFFFU)

/* GPIO配置结构体 */
typedef struct {
    uint8_t Mode;      /* GPIO模式 - DES-002 */
    uint8_t Pull;      /* GPIO上下拉 - DES-003 */
    uint8_t Speed;     /* GPIO速度 - DES-004 */
    uint8_t Alternate; /* 复用功能(暂未使用) */
} GPIO_Config_t;

/* 函数声明 */

/**
 * @brief  初始化GPIO引脚
 * @note   对应Spec:DES-020
 * @param  GPIOx: GPIO端口指针
 * @param  GPIO_Pin: GPIO引脚
 * @param  Config: GPIO配置
 * @retval 0: 成功, -1: 参数错误
 */
int8_t GPIO_Init(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_Config_t *Config);

/**
 * @brief  读取GPIO引脚状态
 * @note   对应Spec:DES-021
 * @param  GPIOx: GPIO端口指针
 * @param  GPIO_Pin: GPIO引脚
 * @param  PinState: 引脚状态输出
 * @retval 0: 成功, -1: 参数错误
 */
int8_t GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, uint8_t *PinState);

/**
 * @brief  写入GPIO引脚状态
 * @note   对应Spec:DES-022
 * @param  GPIOx: GPIO端口指针
 * @param  GPIO_Pin: GPIO引脚
 * @param  PinState: 引脚状态
 * @retval 0: 成功, -1: 参数错误
 */
int8_t GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, uint8_t PinState);

/**
 * @brief  翻转GPIO引脚状态
 * @note   对应Spec:DES-023
 * @param  GPIOx: GPIO端口指针
 * @param  GPIO_Pin: GPIO引脚
 * @retval 0: 成功, -1: 参数错误
 */
int8_t GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin);

#endif /* __GPIO_H */

2.2 源文件实现

/**
 * @file gpio.c
 * @brief GPIO驱动实现
 * @note 对应Spec:GPIO量产驱动
 */

#include "gpio.h"

/**
 * @brief  检查GPIO端口是否有效
 * @note   对应Spec:DES-001, DES-030
 * @param  GPIOx: GPIO端口指针
 * @retval 1: 有效, 0: 无效
 */
static uint8_t GPIO_IsValidPort(GPIO_TypeDef *GPIOx)
{
    if (GPIOx == GPIOA) return 1;
    if (GPIOx == GPIOB) return 1;
    if (GPIOx == GPIOC) return 1;
    if (GPIOx == GPIOD) return 1;
    if (GPIOx == GPIOE) return 1;
    return 0;
}

/**
 * @brief  检查GPIO引脚是否有效
 * @note   对应Spec:DES-011, DES-031
 * @param  GPIO_Pin: GPIO引脚
 * @retval 1: 有效, 0: 无效
 */
static uint8_t GPIO_IsValidPin(uint16_t GPIO_Pin)
{
    if (GPIO_Pin == 0U) return 0;
    if ((GPIO_Pin & 0xFFFF0000U) != 0U) return 0;
    return 1;
}

/**
 * @brief  使能GPIO时钟
 * @note   对应Spec:DES-001
 * @param  GPIOx: GPIO端口指针
 */
static void GPIO_EnableClock(GPIO_TypeDef *GPIOx)
{
    if (GPIOx == GPIOA) {
        RCC->APB2ENR |= RCC_APB2ENR_IOPAEN;
    } else if (GPIOx == GPIOB) {
        RCC->APB2ENR |= RCC_APB2ENR_IOPBEN;
    } else if (GPIOx == GPIOC) {
        RCC->APB2ENR |= RCC_APB2ENR_IOPCEN;
    } else if (GPIOx == GPIOD) {
        RCC->APB2ENR |= RCC_APB2ENR_IOPDEN;
    } else if (GPIOx == GPIOE) {
        RCC->APB2ENR |= RCC_APB2ENR_IOPEEN;
    }
}

/**
 * @brief  初始化GPIO引脚
 * @note   对应Spec:DES-020
 */
int8_t GPIO_Init(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, GPIO_Config_t *Config)
{
    /* 入参检查 - DES-030, DES-031, DES-032 */
    if (GPIOx == NULL) return -1;
    if (Config == NULL) return -1;
    if (GPIO_IsValidPort(GPIOx) == 0U) return -1;
    if (GPIO_IsValidPin(GPIO_Pin) == 0U) return -1;
    
    /* 检查配置有效性 */
    if (Config->Mode > GPIO_MODE_ANALOG) return -1;
    if (Config->Pull > GPIO_PULLDOWN) return -1;
    if (Config->Speed > GPIO_SPEED_VERY_HIGH) return -1;
    
    /* 使能GPIO时钟 */
    GPIO_EnableClock(GPIOx);
    
    /* 计算引脚位置 */
    uint8_t pinpos = 0U;
    uint16_t tmp = GPIO_Pin;
    while ((tmp & 0x0001U) == 0U) {
        tmp >>= 1;
        pinpos++;
    }
    
    /* 配置GPIO模式 - DES-002 */
    uint32_t moder = 0U;
    uint32_t cr = 0U;
    
    if (Config->Mode == GPIO_MODE_INPUT) {
        moder = 0U;  /* 输入模式 */
        if (Config->Pull == GPIO_PULLUP) {
            cr = 0x2U;  /* 上拉输入 */
            GPIOx->ODR |= GPIO_Pin;  /* 设置ODR为1 */
        } else if (Config->Pull == GPIO_PULLDOWN) {
            cr = 0x2U;  /* 下拉输入 */
            GPIOx->ODR &= ~GPIO_Pin;  /* 设置ODR为0 */
        } else {
            cr = 0x0U;  /* 浮空输入 */
        }
    } else if (Config->Mode == GPIO_MODE_OUTPUT_PP) {
        moder = 0x1U;  /* 输出模式 */
        cr = 0x0U;      /* 推挽输出 */
    } else if (Config->Mode == GPIO_MODE_OUTPUT_OD) {
        moder = 0x1U;  /* 输出模式 */
        cr = 0x1U;      /* 开漏输出 */
    } else if (Config->Mode == GPIO_MODE_AF_PP) {
        moder = 0x2U;  /* 复用模式 */
        cr = 0x0U;      /* 推挽复用 */
    } else if (Config->Mode == GPIO_MODE_AF_OD) {
        moder = 0x2U;  /* 复用模式 */
        cr = 0x1U;      /* 开漏复用 */
    } else if (Config->Mode == GPIO_MODE_ANALOG) {
        moder = 0x3U;  /* 模拟模式 */
        cr = 0x0U;
    }
    
    /* 配置GPIO速度 - DES-004 */
    cr |= (uint32_t)(Config->Speed) << 2;
    
    /* 写入配置寄存器 */
    if (pinpos < 8U) {
        /* CRL寄存器 */
        uint32_t tmp_crl = GPIOx->CRL;
        tmp_crl &= ~(0x0FU << (pinpos * 4U));
        tmp_crl |= (cr << (pinpos * 4U));
        GPIOx->CRL = tmp_crl;
    } else {
        /* CRH寄存器 */
        uint32_t tmp_crh = GPIOx->CRH;
        tmp_crh &= ~(0x0FU << ((pinpos - 8U) * 4U));
        tmp_crh |= (cr << ((pinpos - 8U) * 4U));
        GPIOx->CRH = tmp_crh;
    }
    
    return 0;  /* 成功 */
}

/**
 * @brief  读取GPIO引脚状态
 * @note   对应Spec:DES-021
 */
int8_t GPIO_ReadPin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, uint8_t *PinState)
{
    /* 入参检查 - DES-030, DES-031, DES-032 */
    if (GPIOx == NULL) return -1;
    if (PinState == NULL) return -1;
    if (GPIO_IsValidPort(GPIOx) == 0U) return -1;
    if (GPIO_IsValidPin(GPIO_Pin) == 0U) return -1;
    
    /* 读取引脚状态 */
    if ((GPIOx->IDR & GPIO_Pin) != 0U) {
        *PinState = GPIO_PIN_SET;
    } else {
        *PinState = GPIO_PIN_RESET;
    }
    
    return 0;  /* 成功 */
}

/**
 * @brief  写入GPIO引脚状态
 * @note   对应Spec:DES-022
 */
int8_t GPIO_WritePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin, uint8_t PinState)
{
    /* 入参检查 - DES-030, DES-031 */
    if (GPIOx == NULL) return -1;
    if (GPIO_IsValidPort(GPIOx) == 0U) return -1;
    if (GPIO_IsValidPin(GPIO_Pin) == 0U) return -1;
    if (PinState > GPIO_PIN_SET) return -1;
    
    /* 写入引脚状态 */
    if (PinState == GPIO_PIN_SET) {
        GPIOx->BSRR = GPIO_Pin;  /* 置位 */
    } else {
        GPIOx->BRR = GPIO_Pin;   /* 复位 */
    }
    
    return 0;  /* 成功 */
}

/**
 * @brief  翻转GPIO引脚状态
 * @note   对应Spec:DES-023
 */
int8_t GPIO_TogglePin(GPIO_TypeDef *GPIOx, uint16_t GPIO_Pin)
{
    /* 入参检查 - DES-030, DES-031 */
    if (GPIOx == NULL) return -1;
    if (GPIO_IsValidPort(GPIOx) == 0U) return -1;
    if (GPIO_IsValidPin(GPIO_Pin) == 0U) return -1;
    
    /* 翻转引脚状态 */
    GPIOx->ODR ^= GPIO_Pin;
    
    return 0;  /* 成功 */
}

三、GPIO驱动测试

3.1 单元测试代码

/**
 * @file gpio_test.c
 * @brief GPIO驱动单元测试
 * @note 对应Spec:Test层
 */

#include "gpio.h"
#include <stdio.h>

/* 测试结果统计 */
static uint32_t test_pass = 0;
static uint32_t test_fail = 0;

/**
 * @brief  测试GPIO初始化
 * @note   对应Spec:TEST-001
 */
void Test_GPIO_Init(void)
{
    printf("Test GPIO_Init...\r\n");
    
    GPIO_Config_t config = {
        .Mode = GPIO_MODE_OUTPUT_PP,
        .Pull = GPIO_NOPULL,
        .Speed = GPIO_SPEED_HIGH,
        .Alternate = 0
    };
    
    /* 正常初始化 */
    int8_t ret = GPIO_Init(GPIOA, GPIO_PIN_5, &config);
    if (ret == 0) {
        printf("  [PASS] GPIO_Init normal case\r\n");
        test_pass++;
    } else {
        printf("  [FAIL] GPIO_Init normal case\r\n");
        test_fail++;
    }
    
    /* 参数错误:空指针 */
    ret = GPIO_Init(NULL, GPIO_PIN_5, &config);
    if (ret == -1) {
        printf("  [PASS] GPIO_Init NULL port\r\n");
        test_pass++;
    } else {
        printf("  [FAIL] GPIO_Init NULL port\r\n");
        test_fail++;
    }
    
    /* 参数错误:无效引脚 */
    ret = GPIO_Init(GPIOA, 0, &config);
    if (ret == -1) {
        printf("  [PASS] GPIO_Init invalid pin\r\n");
        test_pass++;
    } else {
        printf("  [FAIL] GPIO_Init invalid pin\r\n");
        test_fail++;
    }
}

/**
 * @brief  测试GPIO写入
 * @note   对应Spec:TEST-002
 */
void Test_GPIO_Write(void)
{
    printf("Test GPIO_WritePin...\r\n");
    
    /* 正常写入 */
    int8_t ret = GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET);
    if (ret == 0) {
        printf("  [PASS] GPIO_WritePin normal case\r\n");
        test_pass++;
    } else {
        printf("  [FAIL] GPIO_WritePin normal case\r\n");
        test_fail++;
    }
    
    /* 参数错误:空指针 */
    ret = GPIO_WritePin(NULL, GPIO_PIN_5, GPIO_PIN_SET);
    if (ret == -1) {
        printf("  [PASS] GPIO_WritePin NULL port\r\n");
        test_pass++;
    } else {
        printf("  [FAIL] GPIO_WritePin NULL port\r\n");
        test_fail++;
    }
}

/**
 * @brief  测试GPIO读取
 * @note   对应Spec:TEST-003
 */
void Test_GPIO_Read(void)
{
    printf("Test GPIO_ReadPin...\r\n");
    
    uint8_t state = 0;
    
    /* 正常读取 */
    int8_t ret = GPIO_ReadPin(GPIOA, GPIO_PIN_5, &state);
    if (ret == 0) {
        printf("  [PASS] GPIO_ReadPin normal case, state=%d\r\n", state);
        test_pass++;
    } else {
        printf("  [FAIL] GPIO_ReadPin normal case\r\n");
        test_fail++;
    }
    
    /* 参数错误:空指针 */
    ret = GPIO_ReadPin(NULL, GPIO_PIN_5, &state);
    if (ret == -1) {
        printf("  [PASS] GPIO_ReadPin NULL port\r\n");
        test_pass++;
    } else {
        printf("  [FAIL] GPIO_ReadPin NULL port\r\n");
        test_fail++;
    }
}

/**
 * @brief  测试GPIO翻转
 */
void Test_GPIO_Toggle(void)
{
    printf("Test GPIO_TogglePin...\r\n");
    
    /* 正常翻转 */
    int8_t ret = GPIO_TogglePin(GPIOA, GPIO_PIN_5);
    if (ret == 0) {
        printf("  [PASS] GPIO_TogglePin normal case\r\n");
        test_pass++;
    } else {
        printf("  [FAIL] GPIO_TogglePin normal case\r\n");
        test_fail++;
    }
}

/**
 * @brief  打印测试结果
 */
void Test_PrintResult(void)
{
    printf("\r\n=== Test Result ===\r\n");
    printf("Pass: %d\r\n", test_pass);
    printf("Fail: %d\r\n", test_fail);
    printf("Total: %d\r\n", test_pass + test_fail);
}

/**
 * @brief  运行所有测试
 */
void Test_RunAll(void)
{
    printf("\r\n=== GPIO Driver Test ===\r\n\r\n");
    
    Test_GPIO_Init();
    Test_GPIO_Write();
    Test_GPIO_Read();
    Test_GPIO_Toggle();
    
    Test_PrintResult();
}

四、GPIO驱动使用示例

4.1 LED控制示例

/**
 * @file main.c
 * @brief GPIO驱动使用示例:LED控制
 */

#include "gpio.h"

/* LED引脚定义 */
#define LED1_PORT   GPIOA
#define LED1_PIN    GPIO_PIN_5

#define LED2_PORT   GPIOA
#define LED2_PIN    GPIO_PIN_6

/* LED初始化 */
void LED_Init(void)
{
    GPIO_Config_t config = {
        .Mode = GPIO_MODE_OUTPUT_PP,
        .Pull = GPIO_NOPULL,
        .Speed = GPIO_SPEED_LOW,
        .Alternate = 0
    };
    
    GPIO_Init(LED1_PORT, LED1_PIN, &config);
    GPIO_Init(LED2_PORT, LED2_PIN, &config);
    
    /* 初始状态:LED灭 */
    GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_RESET);
    GPIO_WritePin(LED2_PORT, LED2_PIN, GPIO_PIN_RESET);
}

/* LED点亮 */
void LED_On(uint8_t led_id)
{
    if (led_id == 1) {
        GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_SET);
    } else if (led_id == 2) {
        GPIO_WritePin(LED2_PORT, LED2_PIN, GPIO_PIN_SET);
    }
}

/* LED熄灭 */
void LED_Off(uint8_t led_id)
{
    if (led_id == 1) {
        GPIO_WritePin(LED1_PORT, LED1_PIN, GPIO_PIN_RESET);
    } else if (led_id == 2) {
        GPIO_WritePin(LED2_PORT, LED2_PIN, GPIO_PIN_RESET);
    }
}

/* LED翻转 */
void LED_Toggle(uint8_t led_id)
{
    if (led_id == 1) {
        GPIO_TogglePin(LED1_PORT, LED1_PIN);
    } else if (led_id == 2) {
        GPIO_TogglePin(LED2_PORT, LED2_PIN);
    }
}

/* 主函数 */
int main(void)
{
    /* 初始化LED */
    LED_Init();
    
    while (1) {
        /* LED1翻转 */
        LED_Toggle(1);
        
        /* 延时 */
        for (uint32_t i = 0; i < 1000000; i++);
        
        /* LED2翻转 */
        LED_Toggle(2);
        
        /* 延时 */
        for (uint32_t i = 0; i < 1000000; i++);
    }
}

4.2 按键检测示例

/**
 * @brief 按键检测示例
 */

/* 按键引脚定义 */
#define KEY_PORT    GPIOC
#define KEY_PIN     GPIO_PIN_13

/* 按键初始化 */
void KEY_Init(void)
{
    GPIO_Config_t config = {
        .Mode = GPIO_MODE_INPUT,
        .Pull = GPIO_PULLUP,  /* 上拉输入 */
        .Speed = GPIO_SPEED_LOW,
        .Alternate = 0
    };
    
    GPIO_Init(KEY_PORT, KEY_PIN, &config);
}

/* 按键扫描 */
uint8_t KEY_Scan(void)
{
    uint8_t state = 0;
    
    GPIO_ReadPin(KEY_PORT, KEY_PIN, &state);
    
    if (state == GPIO_PIN_RESET) {
        /* 按键按下(低电平) */
        return 1;
    } else {
        /* 按键释放(高电平) */
        return 0;
    }
}

/* 主函数 */
int main(void)
{
    /* 初始化 */
    LED_Init();
    KEY_Init();
    
    while (1) {
        /* 检测按键 */
        if (KEY_Scan()) {
            /* 按键按下,LED翻转 */
            LED_Toggle(1);
            
            /* 等待按键释放 */
            while (KEY_Scan());
        }
    }
}

五、本讲核心要点

5.1 记住这三句话

  1. GPIO驱动Spec完整定义:需求、设计、编码、测试四层,缺一不可

  2. 参数检查是量产基础:所有函数必须检查参数,防止非法访问

  3. Spec驱动代码生成:先写Spec,再生成代码,保证质量

5.2 Spec驱动开发流程

1. 编写完整Spec
   ↓
2. AI按Spec生成代码
   ↓
3. 代码审查(对照Spec)
   ↓
4. 单元测试(按Spec设计)
   ↓
5. 集成测试
   ↓
6. 量产发布

5.3 下讲预告

第54讲将详细讲解:实战:USART量产收发框架、分包机制Spec落地

如何使用Spec约束USART驱动开发、实现可靠通信?下一讲将详细讲解。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐