1、学习CH04示例程序,包括gpio.c和4个工程中的main.c
(1)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端口和引脚。
(2)GPIO-ASM-STM32L431-20231129工程的main.c
这段代码调用GPIO构件控制小灯闪烁,实现用纯汇编点亮的蓝色发光二极管。代码有数据段定义、代码段定义、主函数、主循环这几个部分。
- 数据段定义
1)定义数据存储data段开始,实际数据存储在RAM中。
2)(定义字符串、变量)
用于输出的字符串(hello_information、light_show1、light_show2和light_show3)、
格式字符串(data_format);
状态变量(mMainLoopCount、mFlag和mLightCount)。
- 代码段定义
定义代码存储text段开始,实际代码存储在Flash中。
.syntax unified //指示下方指令为ARM和thumb通用格式
.thumb //Thumb指令集
.type main function //声明main为函数类型
.global main //将main定义成全局函数,便于芯片初始化之后调用
.align 2 //指令和数据采用2字节对齐,兼容Thumb指令集
- 主函数
1)启动部分(开头)主循环前的初始化工作
比如关总中断、用户外设模块初始化、使能模块中断、开总中断。
2)显示初始化信息
//显示hello_information定义的字符串
ldr r0,=hello_information //待显示字符串首地址
bl printf //调用printf显示字符串
- 主循环
1)主循环通过main_loop标签开始,其中包含了主循环次数的计数和控制LED的逻辑。
2)在每次循环中,主循环次数会加1,并检查是否达到预设的循环次数。
3)一旦达到设定值,则根据mFlag变量的值('L'或'A'),切换LED的状态(亮或暗):
a.如果灯状态为'L',则灯的闪烁次数加1,将灯打开并输出灯的状态信息,然后将状态改为'A';
b.如果灯状态为'A',则将灯关闭,并输出灯的状态信息,然后将状态改为'L'。
(3)GPIO-BlueLight_20230328工程的main.c
这段代码会执行亮红灯操作,只有一个主函数main()。
主函数main():
int main(void)
{
gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF);
printf("程序写入运行后,观察蓝灯:应该亮\nB");
gpio_set(LIGHT_RED,LIGHT_ON); //灯“亮”
//理解蓝色发光二极管为何亮起来了?
for(;;)
{
}}
- 初始化一个名为 LIGHT_RED 的GPIO为输出模式,并设置初始状态为 LIGHT_OFF。
- printf():输出程序的运行状态。
- gpio_set():将LIGHT_RED设置为LIGHT_ON,红灯亮。
- //理解蓝色发光二极管为何亮起来了?
虽然项目名为BlueLight,但是实际上程序写的是 LIGHT_RED(红灯)。
(4)GPIO-Output-Component_STM32L431_20200928工程的main.c
这段代码用构件方法控制一个蓝色发光二极管的闪烁。程序包括主函数和主循环两个部分。
1)主函数
- 声明main函数使用的局部变量,
- 关总中断,
- 给主函数使用的局部变量赋初值,
- 用户外设模块初始化,
- 开总中断,
- printf():输出。
2)主循环
- 使用了一个无限循环for(;;),在该循环内部对主循环次数进行计数,并根据计数值执行LED的亮暗处理。
- 当主循环次数未达到设定值时,继续循环。
- 一旦达到设定值,则根据mFlag变量的值('L'或'A'),切换LED的状态(亮或暗):
a. 如果灯状态为'L',则灯的闪烁次数加1,将灯打开并输出灯的状态信息,然后将状态改为'A';
b.如果灯状态为'A',则将灯关闭,并输出灯的状态信息,然后将状态改为'L'。
(5)GPIO-Output-DirectAddress_STM32L431_20200928工程的main.c
这段代码用直接地址方式进行GPIO的输出。程序包括主函数和主循环两个部分。
1)主函数
- 声明main函数使用的局部变量
- 关总中断
- 给主函数使用的局部变量赋初值
- 用户外设模块初始化(声明变量、变量赋值、GPIO初始化)
- 开总中断
- printf():输出。
2)主循环
使用了无限循环for(;;),循环体内部:
- 主循环次数递增,并检查是否超过特定值。
- 如果超过特定值,通过对 GPIO的B口第9脚的置位/复位寄存器进行操作,来控制蓝灯的亮暗,并通过串口打印输出相应的信息。
- 根据变量mFlag的状态,控制蓝灯的亮暗,并在每次操作后改变mFlag的值,以实现蓝灯的状态切换。
2、给出gpio_set (LIGHT_RED,LIGHT_OFF);语句中LIGHT_RED和LIGHT_OFF的值是多少?贴出每一步的查找截图
如下图:
打开工程的user.h文件,可以看到:
LIGHT_RED的值为(PTB_NUM|7)、LIGHT_OFF的值为1。
那么PTB_NUM的值是什么呢?接下来在gpio.h文件中查找定义,如下图:
PTB_NUM的值为(1<<8),PTB_NUM|7=(1<<8)| 7 = 263.
所以LIGHT_RED的值为263、LIGHT_OFF的值为1.
3、用直接地址编程方式,实现红绿蓝三灯轮流闪烁
在user.h文件中,可以找到红绿蓝三灯的引脚号,如下图:
可看到红绿蓝三灯的引脚号分别为7,8,9。
直接地址编程方式:
首先需要打开GPIO-Output-DirectAddress_STM32L431_20200928工程
添加红灯和绿灯的输出引脚号的定义
第二步:在主循环部分实现红绿蓝轮闪的循环代码。
main.c的源码:
//====================================================================
//文件名称: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='R'; //主循环使用的临时变量:初始为红灯
//(1.4)给全局变量赋初值
//(1.5)用户外设模块初始化
// 红灯:B口7脚,绿灯:B口8脚,蓝灯: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口9脚为输出引脚(令D19、D18=01)方法如下:
//红灯
*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);//0b11111111111100111111111111111111;
*gpio_mode |=(1<<18); //0b00000000000001000000000000000000;
//(思考:为什么这样赋值?答案见本文件末尾注①)
//(1.6)使能模块中断
//(1.7)【不变】开总中断
ENABLE_INTERRUPTS;
printf("-----------------------------------------------------\r\n");
printf("金葫芦提示:直接地址方式进行GPIO输出\r\n");
printf(" 红绿蓝三灯轮流闪烁\r\n");
printf(" 这里是nf的作业\r\n");
printf("----------------------------------------------------\r\n");
//for(;;) { } //在此打桩,理解蓝色发光二极管为何亮起来了?
//(1)======启动部分(结尾)==========================================
//(2)======主循环部分(开头)=========================================
for(;;) //for(;;)(开头)
{
//(2.1)主循环次数+1,并判断是否小于特定常数
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(;;)结尾
//(2)======主循环部分(结尾)========================================
}
运行结果:
直接地址方式实现三灯轮闪。
4、用调用构件方式,实现红绿蓝的八种组合轮流闪烁
用调用构件方式:
打开GPIO-Output-Component_STM32L431_20200928工程,
用户外设模块初始化(初始化红绿蓝三灯)
在循环里面,通过控制红绿蓝三灯的亮暗(有8种组合:0红1绿2蓝3黄4青5紫6白7暗)来编写循环
最终实现八灯轮闪。
main.c的源码:
//======================================================================
//文件名称: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=0; //灯的状态标志
mLightCount=0; //灯的闪烁次数
//(1.4)给全局变量赋初值
//(1.5)用户外设模块初始化
gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_ON); //初始化红灯
gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_ON); //初始化绿灯
gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_ON); //初始化蓝灯
//(1.6)使能模块中断
//(1.7)【不变】开总中断
ENABLE_INTERRUPTS;
printf("------------------------------------------------------\n");
printf("金葫芦提示:构件法输出控制小灯亮暗 \n");
printf("红绿蓝的八种组合轮流闪烁 \n");
printf("这里是nf的作业\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;
//(2.//0红1绿2蓝3黄4青5紫6白7暗(八灯循环)
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_OFF);
gpio_set(LIGHT_GREEN, LIGHT_ON);
gpio_set(LIGHT_BLUE, LIGHT_ON);
printf("青灯:亮\n"); // 输出当前状态
break;
case 5:
gpio_set(LIGHT_RED, LIGHT_ON);
gpio_set(LIGHT_GREEN, LIGHT_OFF);
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(;;)结尾
//(2)======主循环部分(结尾)========================================
} //main函数(结尾)
运行结果如下:
从左到右依次为:(0红1绿2蓝3黄4青5紫6白7暗)
以上就是构件调用方式实现的八灯轮闪。