1、学习CH04示例程序,包括gpio.c和4个工程中的main.c
(1)GPIO-ASM-STM32L431-20231129工程的main.c
这段代码用纯汇编语言控制一个蓝色发光二极管(LED)的闪烁。其主要分为几个部分:数据段定义、代码段定义、主函数、主循环。
①数据段定义(.data)
- 定义数据存储data段开始,实际数据存储在RAM中;
- 在RAM中定义了一系列字符串和变量,包括用于输出的字符串(hello_information、light_show1、light_show2和light_show3)、格式字符串(data_format)以及一些状态变量(mMainLoopCount、mFlag和mLightCount)。
②代码段定义(.text)
- 定义代码存储text段开始,实际代码存储在Flash中;
- 定义了主程序代码。
③主函数(main)
- 初始化部分:这部分包括了一些初始化工作,如关总中断、初始化外设模块(蓝灯和串口UART),以及开总中断。
- 显示初始化信息:使用printf函数输出hello_information定义的字符串。
④主循环(main_loop)
- 主循环通过main_loop标签开始,其中包含了主循环次数的计数和控制LED的逻辑。
- 在每次循环中,主循环次数会加1,并检查是否达到预设的循环次数。
- 一旦达到设定值,则根据mFlag变量的值('L'或'A'),切换LED的状态(亮或暗):
- - 如果灯状态为'L',则灯的闪烁次数加1,将灯打开并输出灯的状态信息,然后将状态改为'A';
- - 如果灯状态为'A',则将灯关闭,并输出灯的状态信息,然后将状态改为'L'。
(2)GPIO-Output-Component_STM32L431_20200928工程的main.c
这段代码用构件方法控制一个蓝色发光二极管(LED)的闪烁。其主要分为主函数和主循环。
①主函数(main)
- 初始化部分:进行了一系列初始化工作,包括初始化局部变量、关闭总中断、给局部变量赋初值、初始化外设模块(蓝灯)以及打印欢迎提示信息。
②主循环(main_loop)
- 使用了一个无限循环for(;;),在该循环内部对主循环次数进行计数,并根据计数值执行LED的亮暗处理。
- 当主循环次数未达到设定值时,继续循环。
- 一旦达到设定值,则根据mFlag变量的值('L'或'A'),切换LED的状态(亮或暗):
- - 如果灯状态为'L',则灯的闪烁次数加1,将灯打开并输出灯的状态信息,然后将状态改为'A';
- - 如果灯状态为'A',则将灯关闭,并输出灯的状态信息,然后将状态改为'L'。
(3)GPIO-Output-DirectAddress_STM32L431_20200928工程的main.c
这段代码使用直接地址方式进行GPIO输出。其主要分为主函数和主循环。
①主函数(main)
- 声明变量:定义了一些volatile修饰的uint32_t类型的变量,分别表示GPIOB的时钟使能寄存器地址、GPIOB的基地址、引脚模式寄存器地址、置位/复位寄存器地址以及GPIO位复位寄存器地址,并且对这些变量进行了相应的赋值。
- GPIO初始化(直接针对寄存器进行)
- - 通过将相应的位设置为1来启用GPIOB的时钟,从而使能GPIOB口的时钟。
- - 通过对引脚模式寄存器的位操作,将B口9脚定义为输出引脚。
②主循环(main_loop)
- 在主循环部分,使用了无限循环for(;;),循环体内部有以下操作:
- - 主循环次数递增,并检查是否超过特定值。
- - 如果超过特定值,通过对 GPIO的B口第9脚的置位/复位寄存器进行操作,来控制蓝灯的亮暗,并通过串口打印输出相应的信息。
- - 根据变量mFlag的状态,控制蓝灯的亮暗,并在每次操作后改变mFlag的值,以实现蓝灯的状态切换。
(4)GPIO-BlueLight_20230328工程的main.c
这段代码主要执行亮红灯操作,主要为一个主函数
主函数 (main)
- 初始化一个名为 LIGHT_RED 的GPIO为输出模式,并设置初始状态为 LIGHT_OFF。
- 通过 printf 输出一段文本,提示程序的运行状态。
- 将 LIGHT_RED 的状态设置为 LIGHT_ON,使得连接到该GPIO的LED灯亮起。
- (虽然项目名称叫BlueLight,但实际写的是红灯,所以红灯亮起)
(5)gpio.c
此代码段是一个针对STM32微控制器的GPIO(通用输入输出)驱动库,提供了对GPIO端口和引脚的配置和控制功能。
①核心功能
- GPIO初始化 (gpio_init): 初始化指定的GPIO端口和引脚,设置为输入或输出模式,并定义初始状态(高电平或低电平)。
- GPIO设置 (gpio_set): 设置指定GPIO端口和引脚的状态(高电平或低电平)。
- GPIO读取 (gpio_get): 读取指定GPIO端口和引脚的当前状态。
- GPIO状态反转 (gpio_reverse): 反转指定GPIO端口和引脚的当前状态。
- GPIO拉配置 (gpio_pull): 配置指定GPIO端口和引脚的上拉或下拉电阻。
- GPIO中断使能与禁用 (gpio_enable_int, gpio_disable_int): 开启或关闭指定GPIO端口和引脚的中断功能,并设置中断触发条件。
- GPIO驱动能力设置 (gpio_drive_strength): 设置指定GPIO端口和引脚的输出驱动能力(低速、中速、高速、超高速)。
- GPIO中断标志获取与清除 (gpio_get_int, gpio_clear_int, gpio_clear_allint): 获取、清除指定GPIO端口和引脚的中断标志,或清除所有端口的GPIO中断。
②辅助功能
- 内部函数 gpio_get_port_pin: 解析传入的端口和引脚编号,用于内部调用,以确定操作的具体GPIO端口和引脚。
③数据结构
- GPIO数组 (GPIO_ARR): 存储STM32各GPIO端口的基地址,方便通过端口号快速访问。
- 中断号表 (table_irq_exti): 映射GPIO扩展中断的IRQ号,用于中断处理函数的配置。
2、给出gpio_set (LIGHT_RED,LIGHT_OFF);语句中LIGHT_RED和LIGHT_OFF的值是多少?贴出每一步的查找截图
打开user.h文件
可以得到LIGHT_OFF值为1,LIGHT_RED的值为(PTB_NUM|7),因此需要找到PTB_NUM的值
打开gpio.h文件
可以得到PTB_NUM的值为(1 << 8),因此LIGHT_RED的值为((1 << 8) | 7)=263
((1 << 8) | 7)运算
1 << 8: 将数字1左移8位。在二进制中,1表示为 00000001,左移8位后变为 0000000100000000,即256。
| 7: 将结果与7进行按位或操作。7在二进制中表示为 00000111。
因此结果为0000000100000111=263
3、用直接地址编程方式,实现红绿蓝三灯轮流闪烁
通过查看user.h文件,可以看到红绿蓝三种灯的引脚号分别为7、8、9
因此,可以在GPIO-Output-DirectAddress_STM32L431_20200928工程的基础上,通过改变引脚号来控制灯的变化
①首先需要更新修改输出引脚的定义
②编写循环,通过变化引脚从而控制灯的变化
代码如下:
#define GLOBLE_VAR
#include "includes.h" //包含总头文件
//----------------------------------------------------------------------
//声明使用到的内部函数
//main.c使用的内部函数声明处
//----------------------------------------------------------------------
//主函数,一般情况下可以认为程序从此开始运行(实际上有启动过程见书稿)
int main(void)
{
//(1)======启动部分(开头)==========================================
printf("这里是小成的嵌入式作业:用直接地址编程方式,实现红绿蓝三灯轮流闪烁。\r\n");
//(1.1)声明main函数使用的局部变量
uint32_t mMainLoopCount; //主循环使用的记录主循环次数变量
uint8_t mFlag; //主循环使用的临时变量
//(1.2)【不变】关总中断
DISABLE_INTERRUPTS;
//(1.3)给主函数使用的局部变量赋初值
mMainLoopCount = 0; //主循环使用的记录主循环次数变量
mFlag = 'R'; //主循环使用的临时变量:初始设置为红灯
//(1.5)用户外设模块初始化
// 红灯连接到B口7脚,绿灯连接到B口8脚,蓝灯连接到B口9脚
volatile uint32_t* RCC_AHB2; //GPIO的B口时钟使能寄存器地址
volatile uint32_t* gpio_ptr; //GPIO的B口基地址
volatile uint32_t* gpio_mode; //引脚模式寄存器地址=口基地址
volatile uint32_t* gpio_bsrr; //置位/复位寄存器地址
volatile uint32_t* gpio_brr; //GPIO位复位寄存器
RCC_AHB2 = (uint32_t*)0x4002104C; //GPIO的B口时钟使能寄存器地址
gpio_ptr = (uint32_t*)0x48000400; //GPIO的B口基地址
gpio_mode = gpio_ptr; //引脚模式寄存器地址=口基地址
gpio_bsrr = gpio_ptr + 6; //置位/复位寄存器地址
gpio_brr = gpio_ptr + 10; //GPIO位复位寄存器
// 使能相应GPIOB的时钟
*RCC_AHB2 |= (1<<1); //GPIOB的B口时钟使能
// 定义B口7, 8, 9脚为输出引脚
*gpio_mode &= ~(3<<14); // 清除B口7脚模式位
*gpio_mode |= (1<<14); // 设置B口7脚为输出
*gpio_mode &= ~(3<<16); // 清除B口8脚模式位
*gpio_mode |= (1<<16); // 设置B口8脚为输出
*gpio_mode &= ~(3<<18); // 清除B口9脚模式位
*gpio_mode |= (1<<18); // 设置B口9脚为输出
//(1.7)【不变】开总中断
ENABLE_INTERRUPTS;
//(2)======主循环部分(开头)=========================================
for(;;) //for(;;)(开头)
{
mMainLoopCount++; //主循环次数+1
if (mMainLoopCount <= 6556677) continue; //如果小于特定常数,继续循环
mMainLoopCount = 0; //清主循环次数
// 切换灯状态
switch (mFlag) {
case 'R': // 红灯亮
printf("红灯:亮\r\n");
*gpio_brr |= (1<<7); // 红灯亮
*gpio_bsrr |= (1<<9); // 蓝灯暗
*gpio_bsrr |= (1<<8); // 绿灯暗
mFlag = 'G'; // 切换到绿灯
break;
case 'G': // 绿灯亮
printf("绿灯:亮\r\n");
*gpio_brr |= (1<<8); // 绿灯亮
*gpio_bsrr |= (1<<7); // 红灯暗
*gpio_bsrr |= (1<<9); // 蓝灯暗
mFlag = 'B'; // 切换到蓝灯
break;
case 'B': // 蓝灯亮
printf("蓝灯:亮\r\n");
*gpio_brr |= (1<<9); // 蓝灯亮
*gpio_bsrr |= (1<<7); // 红灯暗
*gpio_bsrr |= (1<<8); // 绿灯暗
mFlag = 'R'; // 切换回红灯
break;
}
} //for(;;)结尾
}
运行结果:
红绿蓝三灯轮流亮起
4、用调用构件方式,实现红绿蓝的八种组合轮流闪烁
八种组合分别为:①红灯亮;②绿灯亮;③蓝灯亮;④红灯 + 绿灯亮(黄色);⑤红灯 + 蓝灯亮(紫色);⑥绿灯 + 蓝灯亮(青色);⑦红灯 + 绿灯 + 蓝灯亮(白色);⑧所有灯都不亮(暗)
本题可以在GPIO-Output-Component_STM32L431_20200928工程的基础上修改实现
①首先需要更新修改模块初始化
②编写循环,通过控制红绿蓝三灯的亮暗实现三灯的八种组合轮流闪烁
代码为:
#define GLOBLE_VAR
#include "includes.h" //包含总头文件
//----------------------------------------------------------------------
//声明使用到的内部函数
//main.c使用的内部函数声明处
//----------------------------------------------------------------------
//主函数,一般情况下可以认为程序从此开始运行(实际上有启动过程,参见书稿)
int main(void)
{
//(1)======启动部分(开头)==========================================
//(1.1)声明main函数使用的局部变量
uint32_t mMainLoopCount; //主循环次数变量
uint8_t mFlag; //灯的状态标志
uint32_t mLightCount; //灯的状态切换次数
//(1.2)【不变】关总中断
DISABLE_INTERRUPTS;
//(1.3)给主函数使用的局部变量赋初值
mMainLoopCount=0; //主循环次数变量
mFlag=0; //灯的状态标志
mLightCount=0; //灯的闪烁次数
//(1.4)给全局变量赋初值
//(1.5)用户外设模块初始化
gpio_init(LIGHT_BLUE, GPIO_OUTPUT, LIGHT_OFF); //初始化蓝灯
gpio_init(LIGHT_RED, GPIO_OUTPUT, LIGHT_OFF); //初始化红灯
gpio_init(LIGHT_GREEN, GPIO_OUTPUT, LIGHT_OFF); //初始化绿灯
//(1.6)使能模块中断
//(1.7)【不变】开总中断
ENABLE_INTERRUPTS;
printf("这里是小成的嵌入式作业:用调用构件方式,实现红绿蓝的八种组合轮流闪烁。\n");
//asm ("bl .");
//(1)======启动部分(结尾)==========================================
//(2)======主循环部分(开头)========================================
for(;;) //for(;;)(开头)
{
mMainLoopCount++;
if (mMainLoopCount <= 12888999) continue;
mMainLoopCount = 0;
// 切换灯的状态
switch (mFlag) {
case 0:
gpio_set(LIGHT_RED, LIGHT_ON);
gpio_set(LIGHT_GREEN, LIGHT_OFF);
gpio_set(LIGHT_BLUE, LIGHT_OFF);
printf("当前颜色为:红\n"); // 输出当前状态
break;
case 1:
gpio_set(LIGHT_RED, LIGHT_OFF);
gpio_set(LIGHT_GREEN, LIGHT_ON);
gpio_set(LIGHT_BLUE, LIGHT_OFF);
printf("当前颜色为:绿\n"); // 输出当前状态
break;
case 2:
gpio_set(LIGHT_RED, LIGHT_OFF);
gpio_set(LIGHT_GREEN, LIGHT_OFF);
gpio_set(LIGHT_BLUE, LIGHT_ON);
printf("当前颜色为:蓝\n"); // 输出当前状态
break;
case 3:
gpio_set(LIGHT_RED, LIGHT_ON);
gpio_set(LIGHT_GREEN, LIGHT_ON);
gpio_set(LIGHT_BLUE, LIGHT_OFF);
printf("当前颜色为:黄\n"); // 输出当前状态
break;
case 4:
gpio_set(LIGHT_RED, LIGHT_ON);
gpio_set(LIGHT_GREEN, LIGHT_OFF);
gpio_set(LIGHT_BLUE, LIGHT_ON);
printf("当前颜色为:紫\n"); // 输出当前状态
break;
case 5:
gpio_set(LIGHT_RED, LIGHT_OFF);
gpio_set(LIGHT_GREEN, LIGHT_ON);
gpio_set(LIGHT_BLUE, LIGHT_ON);
printf("当前颜色为:青\n"); // 输出当前状态
break;
case 6:
gpio_set(LIGHT_RED, LIGHT_ON);
gpio_set(LIGHT_GREEN, LIGHT_ON);
gpio_set(LIGHT_BLUE, LIGHT_ON);
printf("当前颜色为:白\n"); // 输出当前状态
break;
case 7:
gpio_set(LIGHT_RED, LIGHT_OFF);
gpio_set(LIGHT_GREEN, LIGHT_OFF);
gpio_set(LIGHT_BLUE, LIGHT_OFF);
printf("当前颜色为:暗\n"); // 输出当前状态
break;
}
mFlag = (mFlag + 1) % 8; // 更新状态标志,循环通过八种状态
} //for(;;)结尾
} //main函数(结尾)
运行结果:
红绿蓝的八种组合轮流闪烁,依次为(红、绿、蓝、黄、紫、青、白、暗)