1、学习CH04示例程序,包括gpio.c和4个工程中的main.c
GPIO-ASM-STM32L431-20231129工程
数据段定义 .data
hello_information
: 包含了一些提示性信息的字符串,用于在串口上输出。这些字符串包括一些启动信息和状态提示。data_format
: 定义了一个整数格式的字符串,用于在串口输出数字。light_show1
,light_show2
,light_show3
: 用于显示LED亮灭状态和闪烁次数的字符串。
变量定义
mMainLoopCount
(word
): 主循环计数器,记录主循环执行的次数。mFlag
(byte
): 记录LED状态的标志,'A'代表灭(OFF),'L'代表亮(ON)。mLightCount
(word
): 记录LED闪烁的次数。
代码段定义 .text
-
main
函数: 程序的入口函数,初始化系统后进入主循环。- 初始化了系统和外设,包括配置LED和串口。
- 通过串口输出启动信息
hello_information
。 - 进入
main_loop
主循环。
-
main_loop
标签: 主循环体,用于控制LED的闪烁和输出提示信息。- 检查
mMainLoopCount
是否达到预设循环次数,如果未达到则继续循环。 - 如果达到循环次数,根据
mFlag
的状态('A'或'L'),控制LED的亮灭状态和相应的提示信息输出。 - 更新
mMainLoopCount
。 - 无限循环直到
mMainLoopCount
达到设定的循环次数。
- 检查
详细操作
- 初始化阶段:设置系统、GPIO、串口等。
- 主循环中,通过检查
mMainLoopCount
的值来判断循环次数是否已满足,如果满足则根据mFlag
的状态控制LED的亮灭,并输出相应的提示信息。- 循环计数达到预设的循环次数后,会重置
mMainLoopCount
,改变mFlag
的状态,控制LED状态,并根据状态输出相关信息。该段代码的核心功能是控制LED的状态并通过串口输出相关提示信息,通过循环实现LED的闪烁效果。
GPIO-Output-Component_STM32L431_20200928工程
宏定义及头文件
#define GLOBLE_VAR
#include "includes.h"
#define GLOBLE_VAR
定义了宏GLOBLE_VAR
。- 包含了
"includes.h"
头文件,这个头文件可能包含了一些硬件相关的定义和函数声明。
主函数 main
:
int main(void)
{
gpio_init(LIGHT_RED, GPIO_OUTPUT, LIGHT_OFF);
printf("程序写入运行后,观察蓝灯:应该亮\nB");
gpio_set(LIGHT_RED, LIGHT_ON); // 红色 LED 置为亮
for (;;)
{
// 无限循环,程序停留在这里
}
}
gpio_init(LIGHT_RED, GPIO_OUTPUT, LIGHT_OFF);
:初始化一个红色 LED,设置为输出模式,并且初始状态为熄灭(LIGHT_OFF
)。printf("程序写入运行后,观察蓝灯:应该亮\nB");
:通过串口输出一段提示信息,提示用户观察蓝色 LED 应该会亮起。gpio_set(LIGHT_RED, LIGHT_ON);
:将红色 LED 设置为亮起状态(LIGHT_ON
)。
无限循环 for(;;)
- 程序进入一个无限循环,意味着程序会一直停留在这里,不会继续向下执行其他代码。
GPIO-Output-DirectAddress_STM32L431_20200928工程
宏定义及头文件包含:
#define GLOBLE_VAR
: 定义了宏 GLOBLE_VAR
。
包含了 "includes.h"
头文件,这个头文件可能包含了一些硬件相关的定义和函数声明。
主函数 main
:
局部变量声明及初始化:
uint32_t mMainLoopCount; // 主循环次数变量
uint8_t mFlag; // 灯的状态标志
uint32_t mLightCount; // 灯的闪烁次数
mMainLoopCount = 0; // 主循环次数变量初始化为 0
mFlag = 'A'; // 灯的状态标志初始化为 'A'
mLightCount = 0; // 灯的闪烁次数初始化为 0
中断控制:
DISABLE_INTERRUPTS; // 关闭总中断
ENABLE_INTERRUPTS; // 开启总中断
外设初始化:
gpio_init(LIGHT_BLUE, GPIO_OUTPUT, LIGHT_ON); // 初始化蓝灯为亮
串口输出提示信息:
printf("------------------------------------------------------\n");
printf("金葫芦提示:构件法输出控制小灯亮暗 \n");
printf(" 第一次用构件方法点亮的蓝色发光二极管,\n");
printf(" 这是进行应用编程的第一步,可以在此基础上,\n");
printf(" “照葫芦画瓢”地继续学习实践。\n");
printf(" 例如:改为绿灯;调整闪烁频率等。\n");
printf("------------------------------------------------------\n");
主循环:
for (;;)
{
mMainLoopCount++; // 主循环次数加一
if (mMainLoopCount <= 12888999)
continue; // 如果未达到主循环次数设定值,继续循环
// 达到主循环次数设定值,执行以下处理
mMainLoopCount = 0; // 清零主循环次数变量
if (mFlag == 'L')
{
// 灯的状态为亮时
mLightCount++; // 灯的闪烁次数加一
printf("灯的闪烁次数 mLightCount = %d\n", mLightCount);
mFlag = 'A'; // 灯状态标志改为暗
gpio_set(LIGHT_BLUE, LIGHT_ON); // 灯亮
printf("LIGHT_BLUE:ON--\n"); // 串口输出灯的状态
}
else
{
// 灯的状态为暗时
mFlag = 'L'; // 灯状态标志改为亮
gpio_set(LIGHT_BLUE, LIGHT_OFF); // 灯暗
printf("LIGHT_BLUE:OFF--\n"); // 串口输出灯的状态
}
}
GPIO-BlueLight_20230328工程
宏定义及头文件包含:
#define GLOBLE_VAR
: 定义了宏GLOBLE_VAR
。包含了
"includes.h"
头文件,这个头文件可能包含了一些硬件相关的定义和函数声明。
主函数 main
:
-
局部变量声明及初始化
uint32_t mMainLoopCount; // 主循环使用的记录主循环次数变量
uint8_t mFlag; // 主循环使用的临时变量:蓝灯状态标志
mMainLoopCount = 0; // 主循环次数变量初始化为 0
mFlag = 'A'; // 蓝灯状态标志初始化为 'A'
中断控制:
DISABLE_INTERRUPTS; // 关闭总中断
ENABLE_INTERRUPTS; // 开启总中断
GPIO 外设初始化:
变量声明:
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 位复位寄存器地址
GPIO 初始化:
*RCC_AHB2 |= (1 << 1); // GPIO B 口时钟使能
*gpio_mode &= ~(3 << 18); // 定义 B 口 9 脚为输出引脚
*gpio_mode |= (1 << 18); // 设置 B 口 9 脚为输出模式
主循环:
for (;;)
{
mMainLoopCount++; // 主循环次数加一
if (mMainLoopCount <= 6556677)
continue; // 如果小于特定常数,继续循环
mMainLoopCount = 0; // 主循环次数清零
// 切换灯的状态
if (mFlag == 'A')
{
*gpio_brr |= (1 << 9); // 设置蓝灯亮
printf("蓝灯:亮\r\n"); // 输出调试信息
mFlag = 'L'; // 改变状态标志
}
else
{
*gpio_bsrr |= (1 << 9); // 设置蓝灯暗
printf("蓝灯:暗\r\n"); // 输出调试信息
mFlag = 'A'; // 改变状态标志
}
}
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)
计算 1<<81<<8:
- 1<<81<<8 表示将数字 11 的二进制表示向左移动 8 位。
- 数字 11 的二进制表示为 0000000100000001。
- 向左移动 8 位后的结果是将 0000000100000001 变为 00000001000000000000000100000000,对应的十进制数为 256256。
计算按位或 (∣∣) 操作:
- 现在将 256256(1<<81<<8 的结果)与 77 执行按位或操作。
- 256256 的二进制表示为 00000001000000000000000100000000。
- 77 的二进制表示为 00000000000001110000000000000111。
- 逐位执行按位或操作:
- 0∣1=10∣1=1
- 0∣1=10∣1=1
- 0∣1=10∣1=1
- 0∣0=00∣0=0
- 0∣0=00∣0=0
- 0∣0=00∣0=0
- 0∣0=00∣0=0
- 0∣0=00∣0=0
- 因此,00000001000000000000000100000000 按位或 00000000000001110000000000000111 的结果是 00000001000001110000000100000111。
将结果转换回十进制:
- 00000001000001110000000100000111 的二进制表示对应的十进制数为 263。
3、用直接地址编程方式,实现红绿蓝三灯轮流闪烁
查看user.h时候,很明显我们留意到了红绿蓝三灯的引脚号分别为7、8、9。所以我们只需要在GPIO-Output-DirectAddress_STM32L431_20200928工程改变引脚号即可实现控制灯闪烁的变化。
//红灯
*gpio_mode &=~(3<<14);
*gpio_mode |=(1<<14);
//绿灯
*gpio_mode &=~(3<<16);
*gpio_mode |=(1<<16);
//蓝灯
*gpio_mode &= ~(3<<18); //0b11111111111100111111111111111111;
*gpio_mode |=(1<<18); //0b00000000000001000000000000000000;000;
//====================================================================
//文件名称:main.c(应用工程主函数)
//框架提供:SD-Arm(sumcu.suda.edu.cn)
//版本更新:2017.08, 2020.05
//功能描述:见本工程的<01_Doc>文件夹下Readme.txt文件
//====================================================================
#define GLOBLE_VAR
#include "includes.h" //包含总头文件
//----------------------------------------------------------------------
//声明使用到的内部函数
//main.c使用的内部函数声明处
//----------------------------------------------------------------------
//主函数,一般情况下可以认为程序从此开始运行(实际上有启动过程见书稿)
int main(void)
{
//(1)======启动部分(开头)==========================================
//(1.1)声明main函数使用的局部变量
uint32_t mMainLoopCount; //主循环使用的记录主循环次数变量
uint8_t mFlag; //主循环使用的临时变量
//(1.2)【不变】关总中断
DISABLE_INTERRUPTS;
//(1.3)给主函数使用的局部变量赋初值
mMainLoopCount = 0; //主循环使用的记录主循环次数变量
mFlag='A'; //主循环使用的临时变量:蓝灯状态标志
//(1.4)给全局变量赋初值
//(1.5)用户外设模块初始化
// B口9脚(蓝灯,低电平点亮)
//(1.5.1)声明变量
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位复位寄存器
//(1.5.2)变量赋值
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位复位寄存器
//(1.5.3)GPIO初始化
//(1.5.3.1)使能相应GPIOB的时钟
*RCC_AHB2|=(1<<1); //GPIOB的B口时钟使能
//(1.5.3.1)定义B口7、8、9脚为输出引脚(令D19、D18=01)方法如下:
//红灯
*gpio_mode &=~(3<<14);
*gpio_mode |=(1<<14);
//绿灯
*gpio_mode &=~(3<<16);
*gpio_mode |=(1<<16);
//蓝灯
*gpio_mode &= ~(3<<18); //0b11111111111100111111111111111111;
*gpio_mode |=(1<<18); //0b00000000000001000000000000000000;
//(思考:为什么这样赋值?答案见本文件末尾注①)
//(1.6)使能模块中断
//(1.7)【不变】开总中断
ENABLE_INTERRUPTS;
printf("-----------------------------------------------------\r\n");
printf("金葫芦提示:直接地址方式进行GPIO输出\r\n");
printf(" 这个编程有点难以看懂,使用构件编程就简单多了,\r\n");
printf(" 但是构件制作要经过这一关,因此,我们把构件制作与\r\n");
printf(" 基于构件的编程分成不同过程。学习嵌入式系统,\r\n");
printf(" 以理解GPIO、UART、定时器、Flash、ADC、...\r\n");
printf(" 知识要素为出发点,学会正确运用构件进行应用编程,\r\n");
printf(" 理解和掌握2~3个简单构件的制作方法即可。\r\n");
printf("----------------------------------------------------\r\n");
//for(;;) { } //在此打桩,理解蓝色发光二极管为何亮起来了?
//(1)======启动部分(结尾)==========================================
//(2)======主循环部分(开头)=========================================
mFlag = 0;
for(;;) //for(;;)(开头)
{
//(2.1)主循环次数+1,并判断是否小于特定常数
mMainLoopCount++; //+1
if (mMainLoopCount<=6556677) continue; //如果小于特定常数,继续循环
//(2.2)主循环次数超过特定常数,灯状态进行切换(这样灯会闪烁)
mMainLoopCount=0; //清主循环次数
//切换灯状态
switch (mFlag)
{
case 0: // 红灯
*gpio_brr |= (1 << 7); // 点亮红灯(B7)
*gpio_bsrr |= (1 << 8) | (1 << 9); // 熄灭绿灯(B8)和蓝灯(B9)
printf("红灯:亮\r\n");
mFlag = 1; // 切换到绿灯状态
break;
case 1: // 绿灯
*gpio_brr |= (1 << 8); // 点亮绿灯(B8)
*gpio_bsrr |= (1 << 7) | (1 << 9); // 熄灭红灯(B7)和蓝灯(B9)
printf("绿灯:亮\r\n");
mFlag = 2; // 切换到蓝灯状态
break;
case 2: // 蓝灯
*gpio_brr |= (1 << 9); // 点亮蓝灯(B9)
*gpio_bsrr |= (1 << 7) | (1 << 8); // 熄灭红灯(B7)和绿灯(B8)
printf("蓝灯:亮\r\n");
mFlag = 0; // 切换到红灯状态
break;
default:
break;
}
} //for(;;)结尾
//(2)======主循环部分(结尾)========================================
}
/*
注① 这样做的目的在于更改了D19、D18两位的值,而不改变其他位的值,不这样的话,
可能把不需要改变的位也改变了!
*/
//======以下为主函数调用的子函数存放处=====================================
//======以下为主函数调用的子函数===========================================
//========================================================================
/*
知识要素:
(1)main.c是一个模板,该文件所有代码均不涉及具体的硬件和环境,通过调用构件
实现对硬件的干预。
(2)本文件中对宏GLOBLE_VAR进行了定义,所以在包含"includes.h"头文件时,会定
义全局变量,在其他文件中包含"includes.h"头文件时,
编译时会自动增加extern
*/
4、用调用构件方式,实现红绿蓝的八种组合轮流闪烁
易得八种组合为 ①红灯亮;②绿灯亮;③蓝灯亮;④红灯 + 绿灯亮;⑤红灯 + 蓝灯亮;⑥绿灯 + 蓝灯亮;⑦红灯 + 绿灯 + 蓝灯亮;⑧所有灯都不亮
学过的都明白应该在GPIO-Output-Component_STM32L431_20200928工程中实现本题
先在原有基础上增加模块
再直接通过循环控制三种灯的组合
//======================================================================
//文件名称:main.c(应用工程主函数)
//框架提供:SD-Arm(sumcu.suda.edu.cn)
//版本更新:20191108-20200419
//功能描述:见本工程的..\01_Doc\Readme.txt
//移植规则:【固定】
//======================================================================
#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='A'; //灯的状态标志
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");
printf("金葫芦提示:构件法输出控制小灯亮暗 \n");
printf(" 第一次用构件方法点亮的蓝色发光二极管,\n");
printf(" 这是进行应用编程的第一步,可以在此基础上,\n");
printf(" “照葫芦画瓢”地继续学习实践。\n");
printf(" 例如:改为绿灯;调整闪烁频率等。\n");
printf("------------------------------------------------------\n");
//asm ("bl .");
//for(;;) { } //在此打桩,理解蓝色发光二极管为何亮起来了?
//(1)======启动部分(结尾)==========================================
//(2)======主循环部分(开头)========================================
for(;;) //for(;;)(开头)
{
//(2.1)主循环次数变量+1
mMainLoopCount++;
//(2.2)未达到主循环次数设定值,继续循环
if (mMainLoopCount<=12888999) continue;
//(2.3)达到主循环次数设定值,执行下列语句,进行灯的亮暗处理
//(2.3.1)清除循环次数变量
mMainLoopCount=0;
//根据闪烁次数 mLightCount 确定灯的状态标志 mFlag,范围控制在 0 到 7 之间
mFlag = mLightCount % 8;
//(2.3.2)灯状态标志mFlag为'0',灯的闪烁次数+1并显示,改变灯状态及标志
switch (mFlag)
{
case 0: //红灯亮
gpio_set(LIGHT_RED, LIGHT_ON);
gpio_set(LIGHT_GREEN, LIGHT_OFF);
gpio_set(LIGHT_BLUE, LIGHT_OFF);
printf("红灯亮\r\n");
break;
case 1: //绿灯亮
gpio_set(LIGHT_RED, LIGHT_OFF);
gpio_set(LIGHT_GREEN, LIGHT_ON);
gpio_set(LIGHT_BLUE, LIGHT_OFF);
printf("绿灯亮\r\n");
break;
case 2: //蓝灯亮
gpio_set(LIGHT_RED, LIGHT_OFF);
gpio_set(LIGHT_GREEN, LIGHT_OFF);
gpio_set(LIGHT_BLUE, LIGHT_ON);
printf("蓝灯亮\r\n");
break;
case 3: //红灯 + 绿灯亮(黄色)
gpio_set(LIGHT_RED, LIGHT_ON);
gpio_set(LIGHT_GREEN, LIGHT_ON);
gpio_set(LIGHT_BLUE, LIGHT_OFF);
printf("黄色(红+绿)灯亮\r\n");
break;
case 4: //红灯 + 蓝灯亮(紫色)
gpio_set(LIGHT_RED, LIGHT_ON);
gpio_set(LIGHT_GREEN, LIGHT_OFF);
gpio_set(LIGHT_BLUE, LIGHT_ON);
printf("紫色(红+蓝)灯亮\r\n");
break;
case 5: //绿灯 + 蓝灯亮(青色)
gpio_set(LIGHT_RED, LIGHT_OFF);
gpio_set(LIGHT_GREEN, LIGHT_ON);
gpio_set(LIGHT_BLUE, LIGHT_ON);
printf("青色(绿+蓝)灯亮\r\n");
break;
case 6: //红灯 + 绿灯 + 蓝灯亮(白色)
gpio_set(LIGHT_RED, LIGHT_ON);
gpio_set(LIGHT_GREEN, LIGHT_ON);
gpio_set(LIGHT_BLUE, LIGHT_ON);
printf("白色(红+绿+蓝)灯亮\r\n");
break;
case 7: //所有灯都不亮(暗)
gpio_set(LIGHT_RED, LIGHT_OFF);
gpio_set(LIGHT_GREEN, LIGHT_OFF);
gpio_set(LIGHT_BLUE, LIGHT_OFF);
printf("所有灯都不亮\r\n");
break;
default:
break;
}
//灯的闪烁次数+1
mLightCount++;
}//for(;;)结尾
//(2)======主循环部分(结尾)========================================
} //main函数(结尾)
//======以下为主函数调用的子函数===========================================
//========================================================================
/*
知识要素:
(1)main.c是一个模板,该文件所有代码均不涉及具体的硬件和环境,通过调用构件
实现对硬件的干预。
(2)本文件中对宏GLOBLE_VAR进行了定义,所以在包含"includes.h"头文件时,会定
义全局变量,在其他文件中包含"includes.h"头文件时,
编译时会自动增加extern
*/