前言
本章节主要学了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,那么本次作业就到此为止吧,下次作业再见!