嵌入式课程学习_作业4

前言

本章节主要学了GPIO(全称General-Purpose Input/Output,意为通用输入/输出。它是一种通用型的接口,可以实现一些简单的开关量控制)的相关知识,因此本章的作业也将会围绕这个展开。

作业

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

第一个项目:GPIO-ASM-STM32L431-20231129中的main.s

我们先来研究一下第一个工程,第一个工程是使用汇编实现蓝色小灯的闪烁,我这里打算这样分析,分析一下初始化工作以及亮灭的逻辑。ok,先上代码

//(1.5)用户外设模块初始化
//  初始化蓝灯, r0、r1、r2是gpio_init的入口参数
    ldr r0,=LIGHT_BLUE       //r0指明端口和引脚(用=,因常量>=256,需用ldr)
    movs r1,#GPIO_OUTPUT     //r1指明引脚方向为输出
    movs r2,#LIGHT_OFF       //r2指明引脚的初始状态为亮
    bl  gpio_init            //调用gpio初始化函数

可以看到我们是通过这个先设置寄存器的参数,在调用gpio_init进行设置,我们找到gpio_init的函数定义如下

我们再去找找看看LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF的具体数值是什么吧

//追根溯源找到的情况

//LIGHT_BLUE
.equ LIGHT_BLUE,(PTB_NUM|9)    	//蓝色RUN灯使用的端口/引脚
.equ PTB_NUM,(1<<8)             //偏移量

//GPIO_OUTPUT
.equ GPIO_OUTPUT,(1)

//LIGHT_OFF
.equ LIGHT_OFF,1

有了这些数据,再根据备注,我们可以试着看看这个init干了什么吧,首先是第一个参数,指定的就是端口B的引脚9,也就是蓝色小灯,第二个参数是1,也就是端口设置为输出状态,第三个参数是1,设置为高电平,由于三色灯是低电平亮,故初始设置为灭。其实有这些数据,我们还可以算一算蓝色小灯的引脚地址,我们再给出一些数据

//引脚控制寄存器基地址宏定义(只给出PORTA的引脚控制寄存器PCR0的地址,其他由此计算)
.equ RCC_AHB2ENR_BASE,0x4002104C   //RCC->AHB2ENR寄存器基地址
//GPIO寄存器基地址宏定义(只给出PORTA的输出寄存器PDOR的地址,其他由此计算)
.equ GPIO_BASE,0x48000000          //GPIO寄存器基地址

根据作者给的信息,我们能通过给的公式来做出n口m脚的寄存器的地址,但具体的做法交给gpio.s中的函数在做,我们这里不详细分析了,函数如下图

那么接下来我们分析一下灯亮或者灯灭的代码(至于循环的亮灭,不细看了)

ldr r0,=LIGHT_BLUE        //亮灯
ldr r1,=LIGHT_ON
bl  gpio_set

ldr r0,=LIGHT_BLUE        //暗灯
ldr r1,=LIGHT_OFF
bl  gpio_set

大体如上,是通过gpio_set来进行的,我们找到gpio_set的定义如下

可以看到,gpio_set接受两个参数,一个是r0端口号和引脚号,一个是r1高低电平,这玩意上面已经分析过了,就不再具体说明了。具体的实现同样也在gpio.s中。

第二个项目:GPIO-BlueLight_20230328的main.c

这一个main.c实际上没什么好说的,这个是使用c语言编写的主函数,功能是点亮蓝色小灯(调用了构件)

//----------------------------------------------------------------------
//主函数,一般情况下可以认为程序从此开始运行(实际上有启动过程)
int main(void)
{
	gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF);
	
	printf("程序写入运行后,观察蓝灯:应该亮\nB");
    
    gpio_set(LIGHT_RED,LIGHT_ON);  //灯“亮”
     
    //理解蓝色发光二极管为何亮起来了?
 
   for(;;)
    
    {
    	//死循环
    }     

}

跟上面的第一个汇编差不多,思路都是传入参数,调用gpio_init和gpio_set,然后死循环,注意,由于第二第三第四个项目用的gpio.c是同一份,因此我们会放在后面再分析,这里就先简单过一下吧。

第三个项目:GPIO-Output-Component_STM32L431_20200928的mian.c

这个项目是通过调用构件的方式来控制蓝灯一闪一灭,总体逻辑也比较简单,也更符合用惯高级语言的人的习惯,主循环代码如下

//(2)======主循环部分(开头)========================================
	for(;;)   //for(;;)(开头)
	{
//(2.1)主循环次数变量+1
        mMainLoopCount++;
//(2.2)未达到主循环次数设定值,继续循环
		if (mMainLoopCount<=12888999)  continue;
//(2.3)达到主循环次数设定值,执行下列语句,进行灯的亮暗处理
//(2.3.1)清除循环次数变量
		mMainLoopCount=0; 
//(2.3.2)如灯状态标志mFlag为'L',灯的闪烁次数+1并显示,改变灯状态及标志
		if (mFlag=='L')                    //判断灯的状态标志
		{
			mLightCount++;  
			printf("灯的闪烁次数 mLightCount = %d\n",mLightCount);
			mFlag='A';                       //灯的状态标志
			gpio_set(LIGHT_BLUE,LIGHT_ON);  //灯“亮”
			printf(" LIGHT_BLUE:ON--\n");   //串口输出灯的状态
		}
//(2.3.3)如灯状态标志mFlag为'A',改变灯状态及标志
		else
		{
			mFlag='L';                       //灯的状态标志
			gpio_set(LIGHT_BLUE,LIGHT_OFF); //灯“暗”
			printf(" LIGHT_BLUE:OFF--\n");  //串口输出灯的状态
		}
	}

可以看到利用了一个死循环,循环里面通过标志位mFlag来控制灯的亮灭,具体的亮灭调用gpio_set进行,可以看到接受两个参数,一个是小灯的宏,另一个是灯的亮灭宏,通过mMainLoopCount来控制周期的长短。

第四个项目:GPIO-Output-DirectAddress_STM32L431_20200928的main.c

这个项目是通过直接操作寄存器地址的方式来控制蓝色小灯的亮灭,以下是关键的代码部分

// 变量 
   //(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;  //置位/复位寄存器地址		+6指偏移6个寄存器 等效于+24b(字节) 
    gpio_brr=gpio_ptr+10;  //GPIO位复位寄存器		一样


//循环逻辑
    for(;;)     //for(;;)(开头)
    {
        
        //(2.1)主循环次数+1,并判断是否小于特定常数
        mMainLoopCount++;                         //+1
        if (mMainLoopCount<=6556677)  continue;   //如果小于特定常数,继续循环
        //(2.2)主循环次数超过特定常数,灯状态进行切换(这样灯会闪烁)
        mMainLoopCount=0;      //清主循环次数

        //这里注释的原因是我已经重写了代码逻辑了,所以这里是那个原来的代码
        //切换灯状态
        //if (mFlag=='A')   //若灯状态标志为'A'
        //{
		//    *gpio_brr|=(1<<9);     //设置灯“亮”
        //    printf("蓝灯:亮\r\n");  //通过调试串口输出灯的状态
        //    mFlag='L';             //改变状态标志
        //}
        //else                   //否则,若灯状态标志不为'A'    
        //{
		//    *gpio_bsrr|=(1<<9);     //设置灯“暗”
        //    printf("蓝灯:暗\r\n");   //通过调试串口输出灯的状态
        //    mFlag='A';              //改变状态标志
        //}
    } 

可以看到,可以看到在变量定义的时候能看到前面所说的0x48000400,这个是该芯片B端口的基地址,通过这个地址加上偏移量,我们能得到不同功能的寄存器的地址,这个能方便我们后续的操作,如代码中定义的gpio_bsrr=gpio_ptr+6就是置位复位寄存器的起始地址,查阅官方的参考文档可以得知,如下:

可以看到偏移量是0x18,那我们代码中的+6实际上是偏移6个寄存器,每个寄存器都是4字节,那么就是24字节,24字节实际上换成16位表示,偏移量就是0x18,后面的brr也是如此。实际上我们可以发现在初始化引脚之后,控制灯的亮灭就是通过BSRR和BRR这两个寄存器上灯对应的地址,看到代码中的*gpio_brr|=(1<<9)就是如此,这个就是操作第九位(蓝灯的引脚号是9)。这样就能控制蓝灯亮,至于灭,一样的,无非控制的另外一个寄存器罢了,然后若要控制其它的灯,不难,找准其它两个灯的引脚位置,像这里设置蓝灯一样设置便好。

gpio.c学习分析

上面我们提到,这个gpio.c一样的,所以我们放到最后这里再做统一分析说明。我们这里只分析三样东西,分别是文件开头的定义的数组,gpio_init和gpio_set。

首先是数组,如下

//GPIO口基地址放入常数数据组GPIO_ARR[0]~GPIO_ARR[5]中
GPIO_TypeDef * GPIO_ARR[] =
       {(GPIO_TypeDef *)GPIOA_BASE,(GPIO_TypeDef *)GPIOB_BASE,
		(GPIO_TypeDef *)GPIOC_BASE,(GPIO_TypeDef *)GPIOD_BASE,
		(GPIO_TypeDef *)GPIOE_BASE,(GPIO_TypeDef *)GPIOH_BASE};

首先我们可以看到,这个GPIO_TypeDef是一个指针类型,那么说不定它会有相应的成员?突发奇想,我找了一下这个指针的定义,不找不知道,一找吓一跳,如下

//这里是stm32L431xx.h的内容
typedef struct
{
  __IO uint32_t MODER;       /*!< GPIO port mode register,               Address offset: 0x00      */
  __IO uint32_t OTYPER;      /*!< GPIO port output type register,        Address offset: 0x04      */
  __IO uint32_t OSPEEDR;     /*!< GPIO port output speed register,       Address offset: 0x08      */
  __IO uint32_t PUPDR;       /*!< GPIO port pull-up/pull-down register,  Address offset: 0x0C      */
  __IO uint32_t IDR;         /*!< GPIO port input data register,         Address offset: 0x10      */
  __IO uint32_t ODR;         /*!< GPIO port output data register,        Address offset: 0x14      */
  __IO uint32_t BSRR;        /*!< GPIO port bit set/reset  register,     Address offset: 0x18      */
  __IO uint32_t LCKR;        /*!< GPIO port configuration lock register, Address offset: 0x1C      */
  __IO uint32_t AFR[2];      /*!< GPIO alternate function registers,     Address offset: 0x20-0x24 */
  __IO uint32_t BRR;         /*!< GPIO Bit Reset register,               Address offset: 0x28      */

} GPIO_TypeDef;

我们可以发现,端口拥有的11个寄存器的偏移量全在这里了,也就是说,我们可以通过xx->xx的方式来快速访问寄存器了,甚至这个结构体数组上面包含了6个元素,即6个端口,也就是通过这个数组,我们能访问到所有寄存器的地址了!我们可以找一找GPIOA_BASE等的宏的定义,看看是不是对应地址,如下,同样是在stml431xx.h有

/*!< AHB2 peripherals */
#define GPIOA_BASE            (AHB2PERIPH_BASE + 0x0000UL)
#define GPIOB_BASE            (AHB2PERIPH_BASE + 0x0400UL)
#define GPIOC_BASE            (AHB2PERIPH_BASE + 0x0800UL)
#define GPIOD_BASE            (AHB2PERIPH_BASE + 0x0C00UL)
#define GPIOE_BASE            (AHB2PERIPH_BASE + 0x1000UL)
#define GPIOH_BASE            (AHB2PERIPH_BASE + 0x1C00UL)


#define GPIOA               ((GPIO_TypeDef *) GPIOA_BASE)
#define GPIOB               ((GPIO_TypeDef *) GPIOB_BASE)
#define GPIOC               ((GPIO_TypeDef *) GPIOC_BASE)
#define GPIOD               ((GPIO_TypeDef *) GPIOD_BASE)
#define GPIOE               ((GPIO_TypeDef *) GPIOE_BASE)
#define GPIOH               ((GPIO_TypeDef *) GPIOH_BASE)

可以看到,全是偏移量,不过也正是因为这些宏的存在,能够更加方便我们操作这个开发板(构件)。

接下来我们看gpio_init

//=====================================================================
//函数名称:gpio_init
//函数返回:无
//参数说明:port_pin:(端口号)|(引脚号)(如:(PTB_NUM)|(9) 表示为B口9号脚)
//         dir:引脚方向(0=输入,1=输出,可用引脚方向宏定义)
//         state:端口引脚初始状态(0=低电平,1=高电平)
//功能概要:初始化指定端口引脚作为GPIO引脚功能,并定义为输入或输出,若是输出,
//         还指定初始状态是低电平或高电平
//=====================================================================
void gpio_init(uint16_t port_pin, uint8_t dir, uint8_t state)
{
	GPIO_TypeDef *gpio_ptr;    //声明gpio_ptr为GPIO结构体类型指针
	uint8_t port,pin;    //声明端口port、引脚pin变量
	uint32_t temp;       //临时存放寄存器里的值
	//根据带入参数port_pin,解析出端口与引脚分别赋给port,pin
	gpio_get_port_pin(port_pin,&port,&pin);
	//根据port,给局部变量gpio_ptr赋值(GPIO基地址)
	if(7 == port) //GPIOH
		gpio_ptr = GPIO_ARR[port-2];
	else
		gpio_ptr = GPIO_ARR[port];

	//使能相应GPIO时钟
	RCC->AHB2ENR |= (RCC_AHB2ENR_GPIOAEN<<(port * 1u));

	//清GPIO模式寄存器对应引脚位
	temp = gpio_ptr->MODER;
	temp &= ~(GPIO_MODER_MODE0 << (pin * 2u));

	if(dir == 1)  //定义为输出引脚
	{
		temp |= (GPIO_OUTPUT << (pin * 2u));
		gpio_ptr->MODER = temp;
		gpio_set(port_pin,state);    //调用gpio_set函数,设定引脚初始状态
	}
	else  //定义为输入引脚
	{
		temp |= (GPIO_INPUT << (pin * 2u));
		gpio_ptr->MODER = temp;
	}
}

可以发现,这个gpio_init干的事情实际上跟第一个项目中的gpio.inc中的gpio_init是一样的只不过这个是在c层面,另外一个是汇编层面上,具体解释就不展开了,注释很多,并且值得注意的是,留意到那个指针的用法了吗?这就是刚刚那个结构体做的妙用,不像我们项目四中,还得自己操作地址,这个虽然本质上也是操作地址,但是经过包装之后还是很好看的。

接下来是gpio_set,这个函数用于指定某端口某引脚的状态,具体代码如下

//=====================================================================
//函数名称:gpio_set
//函数返回:无
//参数说明:port_pin:(端口号)|(引脚号)(如:(PTB_NUM)|(9) 表示为B口9号脚)
//          state:希望设置的端口引脚状态(0=低电平,1=高电平)
//功能概要:当指定端口引脚被定义为GPIO功能且为输出时,本函数设定引脚状态
//=====================================================================
void gpio_set(uint16_t port_pin, uint8_t state)
{
	//局部变量声明
	GPIO_TypeDef *gpio_ptr;    //声明port_ptr为GPIO结构体类型指针(首地址)
	uint8_t port,pin;        //声明端口port、引脚pin变量
	//根据带入参数port_pin,解析出端口与引脚分别赋给port,pin
	gpio_get_port_pin(port_pin,&port,&pin);
	//根据port,给局部变量gpio_ptr赋值(GPIO基地址)
	if(7 == port) //GPIOH
		gpio_ptr = GPIO_ARR[port-2];
	else
		gpio_ptr = GPIO_ARR[port];

	//根据state,设置对应引脚状态
	if(1 == state)    //高电平(该引脚对应置位寄存器置1)
		gpio_ptr->BSRR = (uint32_t)(1u<<pin);
	else              //低电平(该引脚对应重置寄存器置1)
		gpio_ptr->BRR = (uint32_t)(1u<<pin);
}

其实也是对项目一中的gpio_set进行包装。也算是项目四的一个拓宽吧。

那么作业的第一点的学习分析就到此为止了。

2、给出 gpio set(LIGHT RED,LIGHT OFF);语句中LIGHT RED和LIGHT OFF的值是多少?贴出每一步的查找截图。

这个简单,这个我在第一点的时候已经分析过了,我们再来做一遍吧。

首先我们是在第三个工程main.c中找到了相关的函数,如下

发现main.c包含的头文件includes.h,我们打开这个文件,如下

发现并没有宏常量定义,然后去user.h,如下

ok!到这里,我们成功的找到了关于各灯以及on off的宏常量定义(虽然在前面就知道了,哈哈),这样我们得知了LIGHT_OFF的值是1,这是因为高电平灯不亮,至于LIGHT_RED就是(PTB|7)这个解释是说在端口B的引脚7处,显然是一个地址,我们想要继续深入下去,那么就去gpio.h找了,如下

ok,可以看到是(1<<8),显然是位运算,那么实际上LIGHT_RED就是((1<<8)|7)那么这个数值有什么用呢,实际上他是配合gpio.c的内部函数gpio_get_port_pin进行解析的,函数如下

//----------------------以下为内部函数存放处-----------------
//=====================================================================
//函数名称:gpio_get_port_pin
//函数返回:无
//参数说明:port_pin:端口号|引脚号(如:(PTB_NUM)|(9) 表示为B口9号脚)
//       port:端口号(传指带出参数)
//       pin:引脚号(0~15,实际取值由芯片的物理引脚决定)(传指带出参数)
//功能概要:将传进参数port_pin进行解析,得出具体端口号与引脚号,分别赋值给port与pin,返回。
//       (例:(PTB_NUM)|(9)解析为PORTB与9,并将其分别赋值给port与pin)。
//=====================================================================
void gpio_get_port_pin(uint16_t port_pin,uint8_t* port,uint8_t* pin)
{
	*port = (port_pin>>8);
	*pin = port_pin;
}

因此,最后,语句中LIGHT_RED和LIGHT_OFF的值分别是

LIGHT_RED = ((1<<8)|7)

LIGHT_OFF = 1

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

这个直接地址编程的方式我们基于第四个项目来进行操作,第四个项目是直接地址编程方式实现的蓝色闪烁,我们在其基础上重写我们自己的逻辑,就可以实现了。

先展示以下没改之前的运行结果。如下

与此同时灯的情况如下

项目四修改前效果

ok,接下来我们根据前面学到的方式,修改main.s,实现需求吧,以下粘贴修改过的部分,就不整个代码放上来了,不然太长了。

//修改main.c
//main函数下大概第29行
    //(1.3)给主函数使用的局部变量赋初值
    mMainLoopCount = 0;     //主循环使用的记录主循环次数变量
    //mFlag='A';              //主循环使用的临时变量:蓝灯状态标志
    
    colorFlag = 10;			//这里我们规定第一位有“1、2、3”分别对应红绿蓝
    						//第二位就是规定“0、1”对应灯的亮灭


//main函数下大概第55行
    //(1.5.3.1)定义B口9脚为输出引脚(令D19、D18=01)方法如下:
    //以下是我的修改,为了实现红绿蓝三灯轮流闪烁
    *gpio_mode &= ~(3<<18);  //0b11111111111100111111111111111111; 
    *gpio_mode |=(1<<18);    //0b00000000000001000000000000000000;
    //定义B口8脚、7脚为输出引脚
    *gpio_mode &= ~(3<<16);
    *gpio_mode |=(1<<16);
    *gpio_mode &= ~(3<<14);
    *gpio_mode |=(1<<14);


//main函数下大概72行
    //注释掉
    //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"); 
    
    //增加
    printf("-----------------------------------------------------\r\n");
    printf("广州大学\r\n");
    printf("直接地址方式进行红绿蓝灯闪烁\r\n");
    printf("32106100071 wyw wyw wyw\r\n");
    printf("----------------------------------------------------\r\n");


//main函数下for循环下大概105行
        //切换灯状态
        //注释掉
        //if (mFlag=='A')   //若灯状态标志为'A'
        //{
		//    *gpio_brr|=(1<<9);     //设置灯“亮”
        //    printf("蓝灯:亮\r\n");  //通过调试串口输出灯的状态
        //    mFlag='L';             //改变状态标志
        //}
        //else                   //否则,若灯状态标志不为'A'    
        //{
		//    *gpio_bsrr|=(1<<9);     //设置灯“暗”
        //    printf("蓝灯:暗\r\n");   //通过调试串口输出灯的状态
        //    mFlag='A';              //改变状态标志
        //}	
        
        //这里是自己写的红绿蓝亮灭逻辑
        if(colorFlag == 10)
        {
        	//设置红灯亮
        	*gpio_brr|=(1<<7);
        	printf("红灯:亮\r\n");
        	colorFlag = 11;
        	continue;
        }
        if(colorFlag == 11)
        {
        	//设置红灯灭
        	*gpio_bsrr|=(1<<7);
        	printf("红灯:灭\r\n");
        	colorFlag = 20;
        	continue;
        }
        if(colorFlag == 20)
        {
        	//设置绿灯亮
        	*gpio_brr|=(1<<8);
        	printf("绿灯:亮\r\n");
        	colorFlag = 21;
        	continue;
        }
        if(colorFlag == 21)
        {
        	//设置绿灯灭
        	*gpio_bsrr|=(1<<8);
        	printf("绿灯:灭\r\n");
        	colorFlag = 30;
        	continue;
        }
        if(colorFlag == 30)
        {
        	//设置蓝灯亮
        	*gpio_brr|=(1<<9);
        	printf("蓝灯:亮\r\n");
        	colorFlag = 31;
        	continue;
        }
        if(colorFlag == 31)
        {
        	//设置蓝灯灭
        	*gpio_bsrr|=(1<<9);
        	printf("蓝灯:灭\r\n");
        	colorFlag = 10;
        	continue;
        }

运行结果为与此同时,小灯情况

项目四修改后效果

可以看到,我们成功成功的改变了源程序中只有蓝色小灯闪烁的情况,将其修改成红绿蓝三灯闪烁,实际上想做的话,也可以用直接地址方式去实现第四点的内容,无非就是一轮处理中把其它灯的逻辑写上来,ok,这里我就不过多分析了,前面第一次学习的时候就已经详细解释过了。(第四个项目的分析)

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

这个任务我们基于项目三的代码进行修改,项目三就是调用构件实现蓝色闪烁,我们在其基础上重写我们自己的逻辑,就可以实现了。

以下是源代码的运行结果

与此同时,小灯情况

项目三修改前效果

那么接下来就是修改的要求了,我们这里设定按照总的小灯要按照“暗(RGB都关)-->红-->绿-->黄(R+G)-->蓝-->紫(R+B)-->青(G+B)-->白(R+G+B)”这样的循环来进行闪烁,ok需求先到这里,接下来放上修改后的代码。(就不放完全的了,不然太多了,只放关键的)

//修改main函数
//新增自己的标志变量,大概在main函数的19行开始
    //(1)======启动部分(开头)==========================================
    //(1.1)声明main函数使用的局部变量
	uint32_t mMainLoopCount;  //主循环次数变量
	//uint8_t  mFlag;           //灯的状态标志
	uint32_t mLightCount;     //灯的状态切换次数
	
	//新增
	uint8_t  groupFlag;
	
    //(1.2)【不变】关总中断
	DISABLE_INTERRUPTS;

    //(1.3)给主函数使用的局部变量赋初值
    mMainLoopCount=0;    //主循环次数变量
	//mFlag='A';           //灯的状态标志
	mLightCount=0;       //灯的闪烁次数
	
	//新增
	groupFlag = 0;		 //8种组合闪烁的标志
					     //0~7分别对应代表暗,红,绿,黄,蓝,紫,青,白



//初始化三盏小灯,大概在main函数的42行开始
    //(1.5)用户外设模块初始化
	//注释掉
	//gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_ON);	//初始化蓝灯
	
	//新增,初始化三盏灯,但初始状态为黑
	gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF);	//红灯
	gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF);	//绿灯
	gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF);	//蓝灯



//main函数大概58行开始,重写输出
    //注释掉
	//printf("------------------------------------------------------\n");   
    //printf("金葫芦提示:构件法输出控制小灯亮暗   \n");
    //printf("    第一次用构件方法点亮的蓝色发光二极管,\n");
    //printf("    这是进行应用编程的第一步,可以在此基础上,\n");
    //printf("   “照葫芦画瓢”地继续学习实践。\n");
    //printf("    例如:改为绿灯;调整闪烁频率等。\n");
    //printf("------------------------------------------------------\n"); 
    
    //新增
    printf("------------------------------------------------------\n");
    printf("广州大学\n");
    printf("构建法输出控制小灯RGB灯8种组合\n");
    printf("计科212 32106100071 wyw wyw wyw\n");
    printf("------------------------------------------------------\n");




//main函数的for函数大概92行开始,重写亮灯逻辑
		//注释掉
        //(2.3.2)如灯状态标志mFlag为'L',灯的闪烁次数+1并显示,改变灯状态及标志
		//if (mFlag=='L')                    //判断灯的状态标志
		//{
		//	mLightCount++;  
		//	printf("灯的闪烁次数 mLightCount = %d\n",mLightCount);
		//	mFlag='A';                       //灯的状态标志
		//	gpio_set(LIGHT_BLUE,LIGHT_ON);  //灯“亮”
		//	printf(" LIGHT_BLUE:ON--\n");   //串口输出灯的状态
		//}
        //(2.3.3)如灯状态标志mFlag为'A',改变灯状态及标志
		//else
		//{
		//	mFlag='L';                       //灯的状态标志
		//	gpio_set(LIGHT_BLUE,LIGHT_OFF); //灯“暗”
		//	printf(" LIGHT_BLUE:OFF--\n");  //串口输出灯的状态
		//}
		
		
		//在这里重写亮灯逻辑
		if(groupFlag == 0)	//黑
		{
			mLightCount++;
			gpio_set(LIGHT_RED,LIGHT_OFF);
			gpio_set(LIGHT_GREEN,LIGHT_OFF);
			gpio_set(LIGHT_BLUE,LIGHT_OFF);
			printf("此时灯的颜色为黑色,闪烁次数为:%d\n",mLightCount);
			groupFlag = 1;
			continue;
		}
		if(groupFlag == 1)	//红
		{
			mLightCount++;
			gpio_set(LIGHT_RED,LIGHT_ON);
			gpio_set(LIGHT_GREEN,LIGHT_OFF);
			gpio_set(LIGHT_BLUE,LIGHT_OFF);
			printf("此时灯的颜色为红色,闪烁次数为:%d\n",mLightCount);
			groupFlag = 2;
			continue;
		}
		if(groupFlag == 2)	//绿
		{
			mLightCount++;
			gpio_set(LIGHT_RED,LIGHT_OFF);
			gpio_set(LIGHT_GREEN,LIGHT_ON);
			gpio_set(LIGHT_BLUE,LIGHT_OFF);
			printf("此时灯的颜色为绿色,闪烁次数为:%d\n",mLightCount);
			groupFlag = 3;
			continue;
		}
		if(groupFlag == 3)	//黄
		{
			mLightCount++;
			gpio_set(LIGHT_RED,LIGHT_ON);
			gpio_set(LIGHT_GREEN,LIGHT_ON);
			gpio_set(LIGHT_BLUE,LIGHT_OFF);
			printf("此时灯的颜色为黄色,闪烁次数为:%d\n",mLightCount);
			groupFlag = 4;
			continue;
		}
		if(groupFlag == 4)	//蓝
		{
			mLightCount++;
			gpio_set(LIGHT_RED,LIGHT_OFF);
			gpio_set(LIGHT_GREEN,LIGHT_OFF);
			gpio_set(LIGHT_BLUE,LIGHT_ON);
			printf("此时灯的颜色为蓝色,闪烁次数为:%d\n",mLightCount);
			groupFlag = 5;
			continue;
		}
		if(groupFlag == 5)	//紫
		{
			mLightCount++;
			gpio_set(LIGHT_RED,LIGHT_ON);
			gpio_set(LIGHT_GREEN,LIGHT_OFF);
			gpio_set(LIGHT_BLUE,LIGHT_ON);
			printf("此时灯的颜色为紫色,闪烁次数为:%d\n",mLightCount);
			groupFlag = 6;
			continue;
		}
		if(groupFlag == 6)	//青
		{
			mLightCount++;
			gpio_set(LIGHT_RED,LIGHT_OFF);
			gpio_set(LIGHT_GREEN,LIGHT_ON);
			gpio_set(LIGHT_BLUE,LIGHT_ON);
			printf("此时灯的颜色为青色,闪烁次数为:%d\n",mLightCount);
			groupFlag = 7;
			continue;
		}
		if(groupFlag == 7)	//白
		{
			mLightCount++;
			gpio_set(LIGHT_RED,LIGHT_ON);
			gpio_set(LIGHT_GREEN,LIGHT_ON);
			gpio_set(LIGHT_BLUE,LIGHT_ON);
			printf("此时灯的颜色为白色,闪烁次数为:%d\n",mLightCount);
			groupFlag = 0;
			continue;
		}
		
	}  

运行结果为

与此同时,小灯情况

项目三修改后情况

可以看到,我们成功的改变了源程序中只有蓝色小灯闪烁的情况,将其修改成红绿蓝组合8种颜色闪烁的结果,不过可能是我手机录视频有点模糊,有几个颜色的变化在镜头下变化的幅度似乎不是很大,不过认真看也能看出区别,那么具体的代码不再分析了,因为思路已经在项目三的分析中给出了。那么这次作业的内容就到这里结束了。

总结

本次作业干的事情是真不少,不过总体做下来还是很爽的,从下午5点研究到差不多12点,总体来说,我们先学习了4个示例程序以及gpio.c。这一部分花的时间是最多的,因为我要搞清楚要用的函数的用法,以及深究一些宏是怎么来的,这个部分除了看代码,还要看那个芯片的参考文档(为了理解直接地址编程),总体下来还是挺不错的,好久没有试过这么专注的做一件事了,不过当我打开那个stm32l431xx.h的头文件,还是被震惊到了,为了方便构件编程,那个头文件上面的宏超过了1万行,说实话还是挺震惊的。然后第一点做好之后,后面二三四点就很容易了,基本上就是看一下就知道要怎么写了,不过在第三第四点中,亲眼看到小灯是按照自己预期的闪烁,还是挺激动的哈。ok,那么本次作业就到此为止吧,下次作业再见!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值