嵌入式系统作业4

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暗)

 

以上就是构件调用方式实现的八灯轮闪。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值