GD 32 流水灯

前言:


通过后面的学习掌握了一些逻辑架构的知识,通过复习的方式将学到的裸机任务架构的知识运用起来,同时巩固前面学到的知识,GPIO的配置等。


开发板上LED引脚使用示意图

注:此次LED灯的点亮凡是是高电平点亮,因为电流是从外部向内部进行流动,GND会引脚的店电平拉低,如果给低电平的话无法形成电势差,电流是无法流动的,给一个高电平,电流在电压的作用下点亮LED灯

第一种实现方式

这种方式类似于STM32的库函数编程,不使用结构体使用直接编写的方式进行配置,可以作为参考对比学习


1.0 初始化LED

cpp 复制代码
void LED_Init(void)
{
	// 开启RCC时钟
	rcu_periph_clock_enable(RCU_GPIOA);
	rcu_periph_clock_enable(RCU_GPIOE);
	rcu_periph_clock_enable(RCU_GPIOF);
    // 初始化gpio
	gpio_init(GPIOA,GPIO_MODE_OUT_PP,GPIO_OSPEED_2MHZ,GPIO_PIN_8);
	gpio_init(GPIOE,GPIO_MODE_OUT_PP,GPIO_OSPEED_2MHZ,GPIO_PIN_6);
	gpio_init(GPIOF,GPIO_MODE_OUT_PP,GPIO_OSPEED_2MHZ,GPIO_PIN_6);
	// 引脚默认电平为低电平
	gpio_bit_reset(GPIOA, GPIO_PIN_8);
	gpio_bit_reset(GPIOE, GPIO_PIN_6);
	gpio_bit_reset(GPIOF, GPIO_PIN_6);
}

2.0 LED点亮熄灭

cpp 复制代码
// 高电平的方式点亮
void LED1_On(void)
{
	gpio_bit_set(GPIOA, GPIO_PIN_8);
}

void LED2_On(void)
{
	gpio_bit_set(GPIOE, GPIO_PIN_6);
}

void LED3_On(void)
{
	gpio_bit_set(GPIOF, GPIO_PIN_6);
}

// 低电平的方式熄灭
void LED1_Off(void)
{
	gpio_bit_reset(GPIOA, GPIO_PIN_8);
}

void LED2_Off(void)
{
	gpio_bit_reset(GPIOE, GPIO_PIN_6);
}

void LED3_Off(void)
{
	gpio_bit_reset(GPIOF, GPIO_PIN_6);
}

3.0 LED 流水灯函数

cpp 复制代码
// LED 灯流水效果函数
void LED_Run(void)
{
	LED1_On();
	DelayNms(1000);
	LED2_On();
	DelayNms(1000);
	LED3_On();
	DelayNms(1000);
	
	LED1_Off();
	LED2_Off();
	LED3_Off();
	DelayNms(1000);
}

4.0 LED灯初始化头文件

cpp 复制代码
#ifndef  _LED_DRV_H_
#define  _LED_DRV_H_
#include <stdint.h>

void LED_Init(void);
void LED1_On(void);
void LED2_On(void);
void LED3_On(void);
void LED1_Off(void);
void LED2_Off(void);
void LED3_Off(void);
void LED_Run(void);

#endif

5.0 在主函数中调用

使用面向对象的编码方式,可以使程序看起来更为的简洁,方便程序的后续移植。

cpp 复制代码
#include <stdint.h>
#include "gd32f30x.h"
#include "led_drv.h"
#include "delay.h"

int main(void)
{
	DelayInit();
	LED_Init();
	while(1)
	{
		LED_Run();
	}
}

** 方式 2 **

第二种实现方式

这种实现方式是配合结构体与for循环的方式进行实现,可以方便后续程序的编码与维护,让程序可移植性更强。


1.0 创建结构体

cpp 复制代码
// 创建结构体
typedef struct
{
	// rcu时钟
	rcu_periph_enum rcu;
	// gpio口
	uint32_t gpio;
	// 对应引脚
	uint32_t pin;
	
}LED_GPIO_t;

2.0 创建结构体数组

cpp 复制代码
// 计算数组大小
#define  LED_NUM_MAX (sizeof(g_gpioList) / sizeof(g_gpioList[0]))

//创建结构体数组
static LED_GPIO_t g_gpioList[] = 
{
	{RCU_GPIOA, GPIOA, GPIO_PIN_8},
	{RCU_GPIOE, GPIOE, GPIO_PIN_6},
	{RCU_GPIOF, GPIOF, GPIO_PIN_6}
};

3.0 初始化GPIO

这里使用for循环遍历,然后将值一个个的赋值给结构体成员变量完成时钟的初始化

cpp 复制代码
void LED_DrvInit(void)
{
	uint8_t i = 0;
	for(i = 0; i < LED_NUM_MAX; i++)
	{
		// 开启rcu时钟
		rcu_periph_clock_enable(g_gpioList[i].rcu);
		// 初始化gpio端口
		gpio_init
		(
			g_gpioList[i].gpio, 
			GPIO_MODE_OUT_PP,
			GPIO_OSPEED_2MHZ, 
			g_gpioList[i].pin
		);
		// 引脚默认值
		gpio_bit_reset(g_gpioList[i].gpio, g_gpioList[i].pin);
	}

}

4.0 点亮LED函数,带参数

cpp 复制代码
// LED_ON 点亮
void LED_No(uint8_t ledNo)
{
	// 判断led编号的值是否大于最大数组长度
	if(ledNo > LED_NUM_MAX)
	{
		return; // 返回值无效
	}
	// 点亮
	gpio_bit_set(g_gpioList[ledNo].gpio, g_gpioList[ledNo].pin);
}

5.0 熄灭LED灯函数,带参数

cpp 复制代码
// LED_OFF熄灭
void LED_Off(uint8_t ledNo)
{
	if (ledNo >= LED_NUM_MAX)
	{
		return;
	}
	gpio_bit_reset(g_gpioList[ledNo].gpio, g_gpioList[ledNo].pin);
}

6.0 流水灯程序代码实现

cpp 复制代码
void LED_StructRun(void)
{
		LED_No(LED1);
		DelayNms(1000);
		LED_No(LED2);
		DelayNms(1000);
		LED_No(LED3);
		DelayNms(1000);
		LED_Off(LED1);
		LED_Off(LED2);
		LED_Off(LED3);
		DelayNms(1000);
}

8.0 #define 定义LED编号

cpp 复制代码
#define  LED1  0
#define  LED2  1
#define  LED3  2

9.0 函数头文件代码

cpp 复制代码
#ifndef  _LED_STRUCTDRV_H_
#define  _LED_STRUCTDRV_H_

#include "stdint.h"

void LED_DrvInit(void);
// LED_ON 点亮
void LED_No(uint8_t ledNo);
// LED_OFF熄灭
void LED_Off(uint8_t ledNo);
// 流水灯
void LED_StructRun(void);

#endif

10.0 主函数调用程序

cpp 复制代码
#include <stdint.h>
#include "gd32f30x.h"
#include "led_drv.h"
#include "delay.h"
#include "led_struct_drv.h"


int main(void)
{
	DelayInit();
	//	LED_Init();
	LED_DrvInit();
	while(1)
	{
		//		LED_Run();
		LED_StructRun();
	}
}

11.0 完整代码展示

第一种方式完整C函数代码

cpp 复制代码
#include "gd32f30x.h"                   // Device header
#include <stdint.h>
#include "led_drv.h"
#include "delay.h"

void LED_Init(void)
{
	// 开启RCC时钟
	rcu_periph_clock_enable(RCU_GPIOA);
	rcu_periph_clock_enable(RCU_GPIOE);
	rcu_periph_clock_enable(RCU_GPIOF);
    // 初始化gpio
	gpio_init(GPIOA,GPIO_MODE_OUT_PP,GPIO_OSPEED_2MHZ,GPIO_PIN_8);
	gpio_init(GPIOE,GPIO_MODE_OUT_PP,GPIO_OSPEED_2MHZ,GPIO_PIN_6);
	gpio_init(GPIOF,GPIO_MODE_OUT_PP,GPIO_OSPEED_2MHZ,GPIO_PIN_6);
	// 引脚默认电平为低电平
	gpio_bit_reset(GPIOA, GPIO_PIN_8);
	gpio_bit_reset(GPIOE, GPIO_PIN_6);
	gpio_bit_reset(GPIOF, GPIO_PIN_6);
}

// 高电平的方式点亮
void LED1_On(void)
{
	gpio_bit_set(GPIOA, GPIO_PIN_8);
}

void LED2_On(void)
{
	gpio_bit_set(GPIOE, GPIO_PIN_6);
}

void LED3_On(void)
{
	gpio_bit_set(GPIOF, GPIO_PIN_6);
}

// 低电平的方式熄灭
void LED1_Off(void)
{
	gpio_bit_reset(GPIOA, GPIO_PIN_8);
}

void LED2_Off(void)
{
	gpio_bit_reset(GPIOE, GPIO_PIN_6);
}

void LED3_Off(void)
{
	gpio_bit_reset(GPIOF, GPIO_PIN_6);
}

// LED 灯流水效果函数
void LED_Run(void)
{
	LED1_On();
	DelayNms(1000);
	LED2_On();
	DelayNms(1000);
	LED3_On();
	DelayNms(1000);
	
	LED1_Off();
	LED2_Off();
	LED3_Off();
	DelayNms(1000);
}

第二种方式完整C函数代码

cpp 复制代码
#include "gd32f30x.h"                   // Device header
#include "stdint.h"
#include "delay.h"

#define  LED1  0
#define  LED2  1
#define  LED3  2

// 创建结构体
typedef struct
{
	// rcu时钟
	rcu_periph_enum rcu;
	// gpio口
	uint32_t gpio;
	// 对应引脚
	uint32_t pin;
	
}LED_GPIO_t;

// 计算数组大小
#define  LED_NUM_MAX (sizeof(g_gpioList) / sizeof(g_gpioList[0]))

//创建结构体数组
static LED_GPIO_t g_gpioList[] = 
{
	{RCU_GPIOA, GPIOA, GPIO_PIN_8},
	{RCU_GPIOE, GPIOE, GPIO_PIN_6},
	{RCU_GPIOF, GPIOF, GPIO_PIN_6}
};

void LED_DrvInit(void)
{
	uint8_t i = 0;
	for(i = 0; i < LED_NUM_MAX; i++)
	{
		// 开启rcu时钟
		rcu_periph_clock_enable(g_gpioList[i].rcu);
		// 初始化gpio端口
		gpio_init
		(
			g_gpioList[i].gpio, 
			GPIO_MODE_OUT_PP,
			GPIO_OSPEED_2MHZ, 
			g_gpioList[i].pin
		);
		// 引脚默认值
		gpio_bit_reset(g_gpioList[i].gpio, g_gpioList[i].pin);
	}

}

// LED_ON 点亮
void LED_No(uint8_t ledNo)
{
	// 判断led编号的值是否大于最大数组长度
	if(ledNo > LED_NUM_MAX)
	{
		return; // 返回值无效
	}
	// 点亮
	gpio_bit_set(g_gpioList[ledNo].gpio, g_gpioList[ledNo].pin);
}

// LED_OFF熄灭
void LED_Off(uint8_t ledNo)
{
	if (ledNo >= LED_NUM_MAX)
	{
		return;
	}
	gpio_bit_reset(g_gpioList[ledNo].gpio, g_gpioList[ledNo].pin);
}

void LED_StructRun(void)
{
		LED_No(LED1);
		DelayNms(1000);
		LED_No(LED2);
		DelayNms(1000);
		LED_No(LED3);
		DelayNms(1000);
		LED_Off(LED1);
		LED_Off(LED2);
		LED_Off(LED3);
		DelayNms(1000);
}

12.0 回调函数基础知识补充

定义:回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调。如果代码立即被执行就称为同步回调,如果在之后晚点的某个时间再执行,则称之为异步回调

函数 F1 调用函数 F2 的时候,函数 F1 通过参数给 函数 F2 传递了另外一个函数 F3 的指针,在函数 F2 执行的过程中,函数F2 调用了函数 F3,这个动作就叫做回调(Callback),而先被当做指针传入、后面又被回调的函数 F3 就是回调函数。

以上定义参考自菜鸟教程

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <easyx.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>


int Sum(int x, int y) 
{
    return x + y;
}

// 创建一个函数,参数是函数指针
void Handle(int (*pSum)(int a, int b)) 
{
    int sum = (*pSum)(1, 2);
    printf("%d\n", sum);
}

int main() {
   
    Handle(Sum);
    return 0;

}

函数的执行结果是3

typedef 可以对函数进行重定义,注意这个时候的写法是表示的含义还是不同的比如以下的案例

typedef unsigned char uchar; 给变量进行从命名,如unsigned char 命名为uchar,

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <easyx.h>
#include <stdio.h>
#include <math.h>
#include <string.h>
#include <stdlib.h>

// 这个时候函数指针变量的名字是 PFUNC
typedef int (*PFUNC)(int a, int b);

int Sum(int x, int y) 
{
    return x + y;
}

// 创建一个函数,参数是函数指针
void Handle(PFUNC pSum)
{
    int sum = (*pSum)(1, 2);
    printf("%d\n", sum);
}

int main() {
   
    Handle(Sum);
    return 0;

}

以上程序的执行结果也是三


...


结尾


目的是通过本次代码的编写巩固之前的知识,同时使用想把裸机程序任务调度的方式,以及回调函数的知识运用在本次的代码当中,方便理解后续的代码知识,以上内容仅供学习参考,后续后继续更细裸机任务调度方案以及回调函数相关的运用。

相关推荐
时光の尘16 分钟前
C语言菜鸟入门·关键字·float以及double的用法
运维·服务器·c语言·开发语言·stm32·单片机·c
嵌入式大圣2 小时前
单片机结合OpenCV
单片机·嵌入式硬件·opencv
日晨难再3 小时前
嵌入式:STM32的启动(Startup)文件解析
stm32·单片机·嵌入式硬件
yufengxinpian4 小时前
集成了高性能ARM Cortex-M0+处理器的一款SimpleLink 2.4 GHz无线模块-RF-BM-2340B1
单片机·嵌入式硬件·音视频·智能硬件
__基本操作__5 小时前
历遍单片机下的IIC设备[ESP--0]
单片机·嵌入式硬件
网易独家音乐人Mike Zhou11 小时前
【卡尔曼滤波】数据预测Prediction观测器的理论推导及应用 C语言、Python实现(Kalman Filter)
c语言·python·单片机·物联网·算法·嵌入式·iot
zy张起灵11 小时前
48v72v-100v转12v 10A大功率转换电源方案CSM3100SK
经验分享·嵌入式硬件·硬件工程
PegasusYu14 小时前
STM32CUBEIDE FreeRTOS操作教程(九):eventgroup事件标志组
stm32·教程·rtos·stm32cubeide·free-rtos·eventgroup·时间标志组
lantiandianzi18 小时前
基于单片机的多功能跑步机控制系统
单片机·嵌入式硬件
文弱书生65618 小时前
输出比较简介
stm32