嵌入式系统作业四·GPIO

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. 计算 1<<81<<8

    • 1<<81<<8 表示将数字 11 的二进制表示向左移动 8 位。
    • 数字 11 的二进制表示为 0000000100000001。
    • 向左移动 8 位后的结果是将 0000000100000001 变为 00000001000000000000000100000000,对应的十进制数为 256256。
  2. 计算按位或 (∣∣) 操作

    • 现在将 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。
  3. 将结果转换回十进制

    • 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
 */

 

  • 14
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值