嵌入式作业4——GPIO直接地址编程方式和调用构件方式实现AHL-STM32L431开发板上红、绿、蓝及组合颜色交替闪烁

此次学习的程序基于“苏州大学嵌入式学习社区官网—金葫芦专区—嵌入式书6版—电子资源AHL-MCU-6”中04-Software\CH04,通过学习参考程序来自行实现GPIO直接地址编程和调用构件编程。


一、学习CH04示例程序,包括gpio.c和4个工程中的main.c

1.四个工程文件如下
在这里插入图片描述
2.通过学习四个工程的main.c文件,知道它们分别用不同的编程方式实现了点亮开发板的灯光或者控制灯光闪烁的功能,包括有

  • 汇编语言编程方式点亮蓝色发光二极管
  • 利用gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF);gpio_set(LIGHT_RED,LIGHT_ON);演示发光二极管如何被点亮
  • 调用构件编程方式实现蓝色发光二极管每秒闪烁一次
  • 直接地址编程方式实现蓝色发光二极管每秒闪烁一次

3.gpio.c在工程文件中目录03_MCU\MCU_drivers下,是GPIO底层驱动构件源文件,里面定义了很多GPIO各功能操作函数如:

  • gpio_init
  • gpio_set
  • gpio_get
  • gpio_reverse
  • gpio_pull
  • gpio_enable_int
  • gpio_disable_int
  • gpio_drive_strength
  • gpio_get_int
  • gpio_clear_int
  • gpio_clear_allint
  • gpio_get_port_pin

二、gpio_set(LIGHT_RED,LIGHT_OFF); 语句中LIGHT_RED和LIGHT_OFF的值是多少?

1.gpio_set(LIGHT_RED,LIGHT_ON);出现在工程目录CH04\GPIO-BlueLight_20240421\07_AppPrg下的main.c文件,具体程序如下:
在这里插入图片描述
2.gpio_set在目录03_MCU\MCU_driversgpio.c文件中定义:LIGHT_RED代表端口号和引脚号,LIGHT_OFF代表的是端口引脚状态
在这里插入图片描述
3.Ctrl+F打开查找功能框,分别输入LIGHT_RED和LIGHT_OFF进行查询其所在文件。

LIGHT_RED和LIGHT_OFF的值都在目录05_UserBoarduser.h文件定义:

  • LIGHT_RED定义为(PTB_NUM|7)即端口B的7号引脚
  • LIGHT_OFF的值为0b0001

在这里插入图片描述
在这里插入图片描述
4.由于只知道PTB_NUM表示端口B,所以继续查询PTB_NUM所在的位置,得出它的值。

PTB_NUM的值在目录03_MCU\MCU_driversgpio.h文件中定义:

  • PTB_NUM的值为(1<<8)即0b10000 0000

在这里插入图片描述
在这里插入图片描述

综上:
LIGHT_RED的值 = PTB_NUM|7 = 0b10000 0111
LIGHT_OFF的值 = 0b0001

三、用直接地址编程方式,实现红绿蓝三灯轮流闪烁

1、GPIO基本编程步骤

GPIO直接地址编程是直接对端口进行编程,使芯片的某一引脚为GPIO功能,并定义为输入/输出,随后进行应用。直接地址编程之前,需要先查阅下载电子资源中目录01-Information下的数据手册,了解几个相关的寄存器:

在这里插入图片描述
(1)通过外部设备时钟使能寄存器(RCC_AHB2ENR)设定对应GPIO端口外部设备时钟使能,本次
编程设定GPIO的端口B外部设备时钟使能

在这里插入图片描述
在这里插入图片描述在这里插入图片描述

通过查阅数据手册得到GPIO的端口B时钟使能寄存器地址为:0x4002104C,并且对时钟使能寄存器第1位置1使能相应的GPIO端口B的时钟。

(2)通过对GPIO模块的端口模式寄存器设定其为GPIO功能,可设定为:输入模式、通用输出模式、复用功能模式、模拟模式,本次编程设定为通用输出模式
在这里插入图片描述

通过查阅数据手册得到只要把端口模式寄存器上对应引脚位置置为01即可表示为输出模式

(3)若是输出引脚,可通过数据输出寄存器设置GPIO端口相应引脚的的输出状态;也可通过置1/复位寄存器设置 GPIO端口相应引脚的输出状态;也可以通过复位寄存器设置相应引脚的输出状态为低电平。本次编程采用置1/复位寄存器
在这里插入图片描述

  • 由于低电平时灯亮,并且置1/复位寄存器的D15-D0位中,0不变,1置1(高电平);D31-D16位中,0不变,1清0(低电平),所以需要将D15-D0位和D31-D16位对应引脚位的值分别置0和1
  • 由步骤(2)得到的红绿蓝三灯分别使用的是PTB7,PTB8,PTB9引脚,所以后续的编程需要分别设置三个引脚位为低电平0:
    BS7(7)=0,BR7(23)=1;//红灯亮
    BS8(8)=0,BR8(25)=1;//绿灯亮
    BS9(9)=0,BR9(25)=1;//蓝灯亮

(4)若是输入引脚,则通过数据输入寄存器获得引脚的状态。若指定位为0,表示当前该引脚上为低电平;若为1,则为高电平。本次编程暂不使用

2、红绿蓝三灯轮流闪烁

目标:红、绿、蓝三灯轮流亮—灭—亮—灭循环
1、已知:B口7脚(红灯,低电平点亮)B口8脚(绿灯,低电平点亮)B口9脚(蓝灯,低电平点亮)

2、声明变量(各寄存器地址变量)

    volatile uint32_t mMainLoopCount;  //主循环使用的记录主循环次数变量
    volatile uint32_t* RCC_AHB2;    //GPIO的B口时钟使能寄存器地址
    volatile uint32_t* gpio_ptr;    //GPIO的B口基地址
    volatile uint32_t* gpio_mode;   //引脚模式寄存器地址=口基地址
	volatile uint32_t* gpio_bsrr;   //置位/复位寄存器地址

3、变量赋值

    RCC_AHB2=(uint32_t*)0x4002104C;   //GPIO的B口时钟使能寄存器地址
	gpio_ptr=(uint32_t*)0x48000400;   //GPIO的B口基地址
	gpio_mode=gpio_ptr;    //引脚模式寄存器地址=口基地址
    gpio_bsrr=gpio_ptr+6;  //置位/复位寄存器地址

4、GPIO初始化

*RCC_AHB2|=(1<<1);       //GPIOB的B口时钟使能
//红灯
//定义B口7脚为输出引脚(令D15、D14=01)
*gpio_mode &=~(3<<14);  //0b11111111111111110011111111111111; 
*gpio_mode|=(1<<14);    //0b00000000000000000100000000000000;
//绿灯
//定义B口8脚为输出引脚(令D17、D16=01)
*gpio_mode &= ~(3<<16);  //0b11111111111111001111111111111111; 
*gpio_mode|=(1<<16);    //0b00000000000000010000000000000000;
//蓝灯
//定义B口9脚为输出引脚(令D19、D18=01)
*gpio_mode &= ~(3<<18);  //0b11111111111100111111111111111111;
*gpio_mode|=(1<<18);    //0b00000000000001000000000000000000;

5、设置灯为亮——采用BSRR寄存器将复位位置1,清零位置0

//红灯亮
*gpio_bsrr|=(1<<23);
*gpio_bsrr &=~(1<<7);
///红灯暗
*gpio_bsrr|=(1<<7);
*gpio_bsrr &=~(1<<23); 
//绿灯亮
*gpio_bsrr|=(1<<24);
*gpio_bsrr &=~(1<<8);
///绿灯暗
*gpio_bsrr|=(1<<8);
*gpio_bsrr &=~(1<<24); 
//蓝灯亮
*gpio_bsrr|=(1<<25);
*gpio_bsrr &=~(1<<9);
//蓝灯暗
*gpio_bsrr|=(1<<9);
*gpio_bsrr &=~(1<<25); 

6、实现红、绿、蓝三灯轮流亮—灭—亮—灭循环

//实现红、绿、蓝三灯轮流闪烁死循环
    while(1)
    {
        //红灯亮
    	//定义B口7脚为输出引脚(令D15、D14=01)
    	*gpio_mode &=~(3<<14);  //0b11111111111111110011111111111111; 
    	*gpio_mode|=(1<<14);    //0b00000000000000000100000000000000;
        *gpio_bsrr|=(1<<23);
        *gpio_bsrr &=~(1<<7);
        printf("红灯:亮\r\n");
        for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		//红灯暗
		*gpio_bsrr|=(1<<7);
        *gpio_bsrr &=~(1<<23);
        printf("红灯:暗\r\n");
        for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
        
        //绿灯亮
        //定义B口8脚为输出引脚(令D17、D16=01)
    	*gpio_mode &= ~(3<<16);  //0b11111111111111001111111111111111; 
    	*gpio_mode|=(1<<16);    //0b00000000000000010000000000000000;
        *gpio_bsrr|=(1<<24);
        *gpio_bsrr &=~(1<<8);
        printf("绿灯:亮\r\n");
        for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		//绿灯暗
		*gpio_bsrr|=(1<<8);
        *gpio_bsrr &=~(1<<24);
        printf("绿灯:暗\r\n");
        for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
        
        //蓝灯亮
        //定义B口9脚为输出引脚(令D19、D18=01)
    	*gpio_mode &= ~(3<<18);  //0b11111111111100111111111111111111;
    	*gpio_mode|=(1<<18);    //0b00000000000001000000000000000000;
        *gpio_bsrr|=(1<<25);
        *gpio_bsrr &=~(1<<9);
        printf("蓝灯:亮\r\n");
        for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		//蓝灯暗
		*gpio_bsrr|=(1<<9);
        *gpio_bsrr &=~(1<<25);
        printf("蓝灯:暗\r\n");
        for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
    }

具体main()函数如下:

int main(void)
{
    volatile uint32_t mMainLoopCount;  //主循环使用的记录主循环次数变量
    
    // 用户外设模块初始化
    // 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;   //置位/复位寄存器地址
	
	//变量赋值
    RCC_AHB2=(uint32_t*)0x4002104C;   //GPIO的B口时钟使能寄存器地址
	gpio_ptr=(uint32_t*)0x48000400;   //GPIO的B口基地址
	gpio_mode=gpio_ptr;    //引脚模式寄存器地址=口基地址
    gpio_bsrr=gpio_ptr+6;  //置位/复位寄存器地址
    
    //GPIO初始化
    //使能相应GPIOB的时钟
    *RCC_AHB2|=(1<<1);       //GPIOB的B口时钟使能

	//实现红、绿、蓝三灯轮流闪烁死循环
    while(1)
    {
        //红灯亮
    	//定义B口7脚为输出引脚(令D15、D14=01)
    	*gpio_mode &=~(3<<14);  //0b11111111111111110011111111111111; 
    	*gpio_mode|=(1<<14);    //0b00000000000000000100000000000000;
        *gpio_bsrr|=(1<<23);
        *gpio_bsrr &=~(1<<7);
        printf("红灯:亮\r\n");
        for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		//红灯暗
		*gpio_bsrr|=(1<<7);
        *gpio_bsrr &=~(1<<23);
        printf("红灯:暗\r\n");
        for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
        
        //绿灯亮
        //定义B口8脚为输出引脚(令D17、D16=01)
    	*gpio_mode &= ~(3<<16);  //0b11111111111111001111111111111111; 
    	*gpio_mode|=(1<<16);    //0b00000000000000010000000000000000;
        *gpio_bsrr|=(1<<24);
        *gpio_bsrr &=~(1<<8);
        printf("绿灯:亮\r\n");
        for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		//绿灯暗
		*gpio_bsrr|=(1<<8);
        *gpio_bsrr &=~(1<<24);
        printf("绿灯:暗\r\n");
        for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
        
        //蓝灯亮
        //定义B口9脚为输出引脚(令D19、D18=01)
    	*gpio_mode &= ~(3<<18);  //0b11111111111100111111111111111111;
    	*gpio_mode|=(1<<18);    //0b00000000000001000000000000000000;
        *gpio_bsrr|=(1<<25);
        *gpio_bsrr &=~(1<<9);
        printf("蓝灯:亮\r\n");
        for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		//蓝灯暗
		*gpio_bsrr|=(1<<9);
        *gpio_bsrr &=~(1<<25);
        printf("蓝灯:暗\r\n");
        for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
    }
}

运行结果:
在这里插入图片描述在这里插入图片描述

用直接地址编程方式,实现红绿蓝三灯轮流闪烁

四、用调用构件方式,实现红绿蓝的八种组合轮流闪烁

  • 目标:红、绿、蓝三灯及其组合共八种灯光轮流亮—灭—亮—灭循环
  • 闪烁顺序:暗、红、绿、黄(红+绿)、蓝、紫(红+蓝)、青(蓝+绿)、白(红+蓝+绿)
  • 用到的GPIO构件中主要API有:

①模块初始化(gpio_init
函数原型:void gpio_init(uint16_t port_pin, uint8_t dir, uint8_t state)
参数说明:
port_pin:(端口号)|(引脚号)(如:(PTB_NUM)|(9) 表示为B口9号脚)
dir:引脚方向(0=输入,1=输出,可用引脚方向宏定义)
state:端口引脚初始状态(0=低电平,1=高电平)

	gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF);	//初始化红灯
	gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF);	//初始化绿灯
	gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF);	//初始化蓝灯

②设置引脚状态(gpio_set
函数原型:void gpio_set(uint16_t port_pin, uint8_t state)
参数说明:
port_pin:(端口号)|(引脚号)(如:(PTB_NUM)|(9) 表示为B口9号脚)
state:希望设置的端口引脚状态(0=低电平,1=高电平)

		gpio_set(LIGHT_RED,LIGHT_ON);  //红灯“亮”
		gpio_set(LIGHT_RED,LIGHT_OFF); //红灯“暗”

		gpio_set(LIGHT_GREEN,LIGHT_ON);  //绿灯“亮”
		gpio_set(LIGHT_GREEN,LIGHT_OFF); //绿灯“暗”
		
		gpio_set(LIGHT_BLUE,LIGHT_ON);  //蓝灯“亮”
		gpio_set(LIGHT_BLUE,LIGHT_OFF); //蓝灯“暗”
		
		//黄灯(红+绿)亮
		gpio_set(LIGHT_RED,LIGHT_ON);
		gpio_set(LIGHT_GREEN,LIGHT_ON);  
		//黄灯暗
		gpio_set(LIGHT_RED,LIGHT_OFF); 
		gpio_set(LIGHT_GREEN,LIGHT_OFF); 
	
		//紫灯(红+蓝)亮
		gpio_set(LIGHT_RED,LIGHT_ON);  
		gpio_set(LIGHT_BLUE,LIGHT_ON);  
		//紫灯暗
		gpio_set(LIGHT_RED,LIGHT_OFF); 
		gpio_set(LIGHT_BLUE,LIGHT_OFF); 

		//青灯(绿+蓝)亮
		gpio_set(LIGHT_GREEN,LIGHT_ON);  
		gpio_set(LIGHT_BLUE,LIGHT_ON);  
		//青灯暗
		gpio_set(LIGHT_GREEN,LIGHT_OFF); 
		gpio_set(LIGHT_BLUE,LIGHT_OFF); 
		
		//白灯(红+绿+蓝)亮
		gpio_set(LIGHT_RED,LIGHT_ON); 
		gpio_set(LIGHT_GREEN,LIGHT_ON);  
		gpio_set(LIGHT_BLUE,LIGHT_ON);  
		//白灯暗
		gpio_set(LIGHT_RED,LIGHT_OFF); 
		gpio_set(LIGHT_GREEN,LIGHT_OFF);
		gpio_set(LIGHT_BLUE,LIGHT_OFF);  
  • 具体实现main()
int main(void)
{
	volatile uint32_t mMainLoopCount;  //主循环次数变量
	
	gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF);	//初始化红灯
	gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF);	//初始化绿灯
	gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF);	//初始化蓝灯
	
	while(1)
	{
		gpio_set(LIGHT_RED,LIGHT_ON);  //红灯“亮”
		printf("红灯:亮\n");   //串口输出灯的状态		
		for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		gpio_set(LIGHT_RED,LIGHT_OFF); //红灯“暗”
		printf("红灯:暗\n");  //串口输出灯的状态		

		for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		gpio_set(LIGHT_GREEN,LIGHT_ON);  //绿灯“亮”
		printf("绿灯:亮\n");   //串口输出灯的状态
		for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		gpio_set(LIGHT_GREEN,LIGHT_OFF); //绿灯“暗”
		printf("绿灯:暗\n");  //串口输出灯的状态
		
		for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		gpio_set(LIGHT_BLUE,LIGHT_ON);  //蓝灯“亮”
		printf("蓝灯:亮\n");   //串口输出灯的状态
		for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		gpio_set(LIGHT_BLUE,LIGHT_OFF); //蓝灯“暗”
		printf("蓝灯:暗\n");  //串口输出灯的状态
		
		for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		//黄灯亮
		gpio_set(LIGHT_RED,LIGHT_ON);
		gpio_set(LIGHT_GREEN,LIGHT_ON);  
		printf("红+绿=黄灯:亮\n");   //串口输出灯的状态
		for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		//黄灯暗
		gpio_set(LIGHT_RED,LIGHT_OFF); 
		gpio_set(LIGHT_GREEN,LIGHT_OFF); 
		printf("红+绿=黄灯:暗\n");  //串口输出灯的状态
		
		for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		//紫灯亮
		gpio_set(LIGHT_RED,LIGHT_ON);  
		gpio_set(LIGHT_BLUE,LIGHT_ON);  
		printf("红+蓝=紫灯:亮\n");   //串口输出灯的状态
		for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		//紫灯暗
		gpio_set(LIGHT_RED,LIGHT_OFF); 
		gpio_set(LIGHT_BLUE,LIGHT_OFF); 
		printf("红+蓝=紫灯:暗\n");  //串口输出灯的状态
		
		for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		//青灯亮
		gpio_set(LIGHT_GREEN,LIGHT_ON);  
		gpio_set(LIGHT_BLUE,LIGHT_ON);  
		printf("绿+蓝=青灯:亮\n");   //串口输出灯的状态
		for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		//青灯暗
		gpio_set(LIGHT_GREEN,LIGHT_OFF); 
		gpio_set(LIGHT_BLUE,LIGHT_OFF); 
		printf("绿+蓝=青灯:暗\n");  //串口输出灯的状态
		
		for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		//白灯亮
		gpio_set(LIGHT_RED,LIGHT_ON); 
		gpio_set(LIGHT_GREEN,LIGHT_ON);  
		gpio_set(LIGHT_BLUE,LIGHT_ON);  
		printf("红+绿+蓝=白灯:亮\n");   //串口输出灯的状态
		for(mMainLoopCount=0;mMainLoopCount<=10000000;mMainLoopCount++){}
		//白灯暗
		gpio_set(LIGHT_RED,LIGHT_OFF); 
		gpio_set(LIGHT_GREEN,LIGHT_OFF);
		gpio_set(LIGHT_BLUE,LIGHT_OFF);  
		printf("红+绿+蓝=白灯:暗\n");  //串口输出灯的状态
	}
}
  • 运行结果
    在这里插入图片描述在这里插入图片描述

    用调用构件方式,实现红绿蓝的八种组合轮流闪烁

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值