嵌入式作业4:AHL-STM32L431开发板上的红绿蓝交替闪烁实现——GPIO直接地址编程与调用构件方式详解
此次学习的程序基于“苏州大学嵌入式学习社区官网—金葫芦专区—嵌入式书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_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_drivers下gpio.c文件中定义:LIGHT_RED代表端口号和引脚号,LIGHT_OFF代表的是端口引脚状态
3.Ctrl+F打开查找功能框,分别输入LIGHT_RED和LIGHT_OFF进行查询其所在文件。
LIGHT_RED和LIGHT_OFF的值都在目录05_UserBoard下user.h文件定义:
4.由于只知道PTB_NUM表示端口B,所以继续查询PTB_NUM所在的位置,得出它的值。
PTB_NUM的值在目录03_MCU\MCU_drivers下gpio.h文件中定义:
综上:
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_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);
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"); //串口输出灯的状态
}
}


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