第53讲:实战——标准GPIO量产驱动,Spec约束零BUG
·
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 记住这三句话
-
GPIO驱动Spec完整定义:需求、设计、编码、测试四层,缺一不可
-
参数检查是量产基础:所有函数必须检查参数,防止非法访问
-
Spec驱动代码生成:先写Spec,再生成代码,保证质量
5.2 Spec驱动开发流程
1. 编写完整Spec
↓
2. AI按Spec生成代码
↓
3. 代码审查(对照Spec)
↓
4. 单元测试(按Spec设计)
↓
5. 集成测试
↓
6. 量产发布
5.3 下讲预告
第54讲将详细讲解:实战:USART量产收发框架、分包机制Spec落地
如何使用Spec约束USART驱动开发、实现可靠通信?下一讲将详细讲解。
更多推荐


所有评论(0)