前言:
本博客仅在应用函数指针与回调函数相关的知识,通过实践巩固所学知识,也是对相关知识点的回顾。
函数指针与回调函数:1-6 GD32函数指针与回调函数-CSDN博客
目录
[1.0 程序架构思想](#1.0 程序架构思想)
[2.0 构建串口结构体](#2.0 构建串口结构体)
[3.0 结构体数组](#3.0 结构体数组)
[4.0 GPIO初始化](#4.0 GPIO初始化)
[5.0 串口初始化](#5.0 串口初始化)
[6.0 中断服务函数](#6.0 中断服务函数)
[7.0 打印输出重定向](#7.0 打印输出重定向)
[8.0 函数指针变量](#8.0 函数指针变量)
[9.0 注册回调函数](#9.0 注册回调函数)
[10.0 回调函数讲解](#10.0 回调函数讲解)
[11.0 灯的编号与状态](#11.0 灯的编号与状态)
[12.0 判断数据完整性](#12.0 判断数据完整性)
[13.0 CRC校验](#13.0 CRC校验)
[14.0 LED处理函数](#14.0 LED处理函数)
[15.0 Usb2Com任务处理函数](#15.0 Usb2Com任务处理函数)
[16.0 USART应用初始化](#16.0 USART应用初始化)
[17.0 usb2com_drv.c](#17.0 usb2com_drv.c)
[18.0 usb2com_app.c](#18.0 usb2com_app.c)
[19.0 main.c](#19.0 main.c)
1.0 程序架构思想
2.0 构建串口结构体
cpp
typedef struct
{
uint32_t uartNo; // 表示串口USART0
rcu_periph_enum rcuUart; // 串口时钟
rcu_periph_enum rcuGpio; // GPIO时钟
uint32_t gpio; // 对应的GPIO口
uint32_t txPin; // 串口发送端引脚
uint32_t rxPin; // 串口接收端引脚
uint32_t irq; // 串口中断
}UartLxInfo_t;
3.0 结构体数组
cpp
static UartLxInfo_t g_uartLxInfo = // 结构体数组,存放结构体参数值
{
USART0, RCU_USART0, RCU_GPIOA, GPIOA, GPIO_PIN_9, GPIO_PIN_10, USART0_IRQn
};
参数具体含义参考定义
4.0 GPIO初始化
cpp
static void Usb2ComGpioInit(void) // GPIO时钟初始化
{
rcu_periph_clock_enable(g_uartLxInfo.rcuGpio);
gpio_init(g_uartLxInfo.gpio, GPIO_MODE_AF_PP, GPIO_OSPEED_10MHZ, g_uartLxInfo.txPin);
gpio_init(g_uartLxInfo.gpio, GPIO_MODE_IPU, GPIO_OSPEED_10MHZ, g_uartLxInfo.rxPin);
}
5.0 串口初始化
cpp
static void Usb2ComUartInit(uint32_t baudRate)
{
rcu_periph_clock_enable(g_uartLxInfo.rcuUart); // 使能串口时钟
usart_deinit(g_uartLxInfo.uartNo); // 复位串口
usart_baudrate_set(g_uartLxInfo.uartNo, baudRate); // 设置波特率
usart_transmit_config(g_uartLxInfo.uartNo, USART_TRANSMIT_ENABLE); // 使能串口发送
usart_receive_config(g_uartLxInfo.uartNo, USART_RECEIVE_ENABLE); // 使能串口接收
usart_interrupt_enable(g_uartLxInfo.uartNo, USART_INT_RBNE); // 使能串口中断
nvic_irq_enable(g_uartLxInfo.irq, 0, 0); // 中断使能
usart_enable(g_uartLxInfo.uartNo); // 串口使能
}
6.0 中断服务函数
cpp
void USART0_IRQHandler(void)
{
if (usart_interrupt_flag_get(g_uartLxInfo.uartNo, USART_INT_FLAG_RBNE) != RESET)
{
usart_interrupt_flag_clear(g_uartLxInfo.uartNo, USART_INT_FLAG_RBNE);
uint8_t uData = (uint8_t)usart_data_receive(g_uartLxInfo.uartNo);
pPrcoUartDataFunc(uData);
}
}
7.0 打印输出重定向
cpp
int fputc(int ch, FILE *f)
{
usart_data_transmit(g_uartLxInfo.uartNo, (uint8_t)ch);
while(usart_flag_get(g_uartLxInfo.uartNo, USART_FLAG_TBE) == RESET);
return ch;
}
8.0 函数指针变量
cpp
static void (*pPrcoUartDataFunc)(uint8_t data); // 函数指针变量,保存应用层回调函数地址
9.0 注册回调函数
cpp
void regUsb2ComCb(void(*pFunc)(uint8_t data))
{
pPrcoUartDataFunc = pFunc;
}
10.0 回调函数讲解
usb2com_app.c
cpp
/**
***********************************************************************
包格式:帧头0 帧头1 数据长度 功能字 LED编号 亮/灭 异或校验数据
0x55 0xAA 0x03 0x06 0x00 0x01 0xFB
***********************************************************************
*/
#define FRAME_HEAD_0 0x55
#define FRAME_HEAD_1 0xAA
#define CTRL_DATA_LEN 3
#define PACKET_DATA_LEN (CTRL_DATA_LEN + 4)
#define FUNC_DATA_IDX 3
#define LED_CTRL_CODE 0x06
#define MAX_BUF_SIZE 20
static uint8_t g_rcvDataBuf[MAX_BUF_SIZE];
static bool g_pktRcvd = false;
11.0 灯的编号与状态
cpp
typedef struct
{
uint8_t ledNo;
uint8_t ledState;
}LedCtrlInfo_t;
12.0 判断数据完整性
cpp
static void ProcUartData(uint8_t data)
{
static uint8_t index = 0;
g_rcvDataBuf[index++] = data;
switch (index)
{
case 1:
if (g_rcvDataBuf[0] != FRAME_HEAD_0)
{
index = 0;
}
break;
case 2:
if (g_rcvDataBuf[1] != FRAME_HEAD_1)
{
index = 0;
}
break;
case PACKET_DATA_LEN:
g_pktRcvd = true;
index = 0;
break;
default:
break;
}
}
13.0 CRC校验
cpp
/**
***********************************************************
* @brief 对数据进行异或运算
* @param data, 存储数组的首地址
* @param len, 要计算的元素的个数
* @return 异或运算结果
***********************************************************
*/
static uint8_t CalXorSum(const uint8_t *data, uint32_t len)
{
uint8_t xorSum = 0;
for (uint32_t i = 0; i < len; i++)
{
xorSum ^= data[i];
}
return xorSum;
}
14.0 LED处理函数
注:该函数调用LED处理函数,对LED灯进行点亮和熄灭操作,使用三元运算符
cpp
/**
***********************************************************
* @brief LED控制处理函数
* @param ctrlData,结构体指针,传入LED的编号和状态
* @return
***********************************************************
*/
static void CtrlLed(LedCtrlInfo_t *ctrlData)
{
ctrlData->ledState != 0 ? TurnOnLed(ctrlData->ledNo) : TurnOffLed(ctrlData->ledNo);
}
15.0 Usb2Com任务处理函数
cpp
void Usb2ComTask(void)
{
if (!g_pktRcvd)
{
return;
}
g_pktRcvd = false;
if (CalXorSum(g_rcvDataBuf, PACKET_DATA_LEN - 1) != g_rcvDataBuf[PACKET_DATA_LEN - 1])
{
return;
}
if (g_rcvDataBuf[FUNC_DATA_IDX] == LED_CTRL_CODE)
{
CtrlLed((LedCtrlInfo_t *)(&g_rcvDataBuf[FUNC_DATA_IDX + 1]));
}
}
16.0 USART应用初始化
cpp
/**
***********************************************************
* @brief USB转串口应用初始化函数
* @param
* @return
***********************************************************
*/
void Usb2ComAppInit(void)
{
regUsb2ComCb(ProcUartData);
}
17.0 usb2com_drv.c
cpp
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include "gd32f30x.h"
typedef struct
{
uint32_t uartNo; // 表示串口USART0
rcu_periph_enum rcuUart; // 串口时钟
rcu_periph_enum rcuGpio; // GPIO时钟
uint32_t gpio; // 对应的GPIO口
uint32_t txPin; // 串口发送端引脚
uint32_t rxPin; // 串口接收端引脚
uint32_t irq; // 串口中断
}UartLxInfo_t;
static UartLxInfo_t g_uartLxInfo = // 结构体数组,存放结构体参数值
{
USART0, RCU_USART0, RCU_GPIOA, GPIOA, GPIO_PIN_9, GPIO_PIN_10, USART0_IRQn
};
static void (*pPrcoUartDataFunc)(uint8_t data); // 函数指针变量,保存应用层回调函数地址
static void Usb2ComGpioInit(void) // GPIO时钟初始化
{
rcu_periph_clock_enable(g_uartLxInfo.rcuGpio);
gpio_init(g_uartLxInfo.gpio, GPIO_MODE_AF_PP, GPIO_OSPEED_10MHZ, g_uartLxInfo.txPin);
gpio_init(g_uartLxInfo.gpio, GPIO_MODE_IPU, GPIO_OSPEED_10MHZ, g_uartLxInfo.rxPin);
}
void regUsb2ComCb(void(*pFunc)(uint8_t data))
{
pPrcoUartDataFunc = pFunc;
}
static void Usb2ComUartInit(uint32_t baudRate)
{
rcu_periph_clock_enable(g_uartLxInfo.rcuUart); // 使能串口时钟
usart_deinit(g_uartLxInfo.uartNo); // 复位串口
usart_baudrate_set(g_uartLxInfo.uartNo, baudRate); // 设置波特率
usart_transmit_config(g_uartLxInfo.uartNo, USART_TRANSMIT_ENABLE); // 使能串口发送
usart_receive_config(g_uartLxInfo.uartNo, USART_RECEIVE_ENABLE); // 使能串口接收
usart_interrupt_enable(g_uartLxInfo.uartNo, USART_INT_RBNE); // 使能串口中断
nvic_irq_enable(g_uartLxInfo.irq, 0, 0); // 中断使能
usart_enable(g_uartLxInfo.uartNo); // 串口使能
}
void Usb2ComDrvInit(void)
{
Usb2ComGpioInit();
Usb2ComUartInit(115200);
}
void USART0_IRQHandler(void)
{
if (usart_interrupt_flag_get(g_uartLxInfo.uartNo, USART_INT_FLAG_RBNE) != RESET)
{
usart_interrupt_flag_clear(g_uartLxInfo.uartNo, USART_INT_FLAG_RBNE);
uint8_t uData = (uint8_t)usart_data_receive(g_uartLxInfo.uartNo);
pPrcoUartDataFunc(uData);
}
}
int fputc(int ch, FILE *f)
{
usart_data_transmit(g_uartLxInfo.uartNo, (uint8_t)ch);
while(usart_flag_get(g_uartLxInfo.uartNo, USART_FLAG_TBE) == RESET);
return ch;
}
18.0 usb2com_app.c
cpp
#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include "usb2com_drv.h"
#include "led_drv.h"
/**
***********************************************************************
包格式:帧头0 帧头1 数据长度 功能字 LED编号 亮/灭 异或校验数据
0x55 0xAA 0x03 0x06 0x00 0x01 0xFB
***********************************************************************
*/
#define FRAME_HEAD_0 0x55
#define FRAME_HEAD_1 0xAA
#define CTRL_DATA_LEN 3
#define PACKET_DATA_LEN (CTRL_DATA_LEN + 4)
#define FUNC_DATA_IDX 3
#define LED_CTRL_CODE 0x06
#define MAX_BUF_SIZE 20
static uint8_t g_rcvDataBuf[MAX_BUF_SIZE];
static bool g_pktRcvd = false;
typedef struct
{
uint8_t ledNo;
uint8_t ledState;
}LedCtrlInfo_t;
static void ProcUartData(uint8_t data)
{
static uint8_t index = 0;
g_rcvDataBuf[index++] = data;
switch (index)
{
case 1:
if (g_rcvDataBuf[0] != FRAME_HEAD_0)
{
index = 0;
}
break;
case 2:
if (g_rcvDataBuf[1] != FRAME_HEAD_1)
{
index = 0;
}
break;
case PACKET_DATA_LEN:
g_pktRcvd = true;
index = 0;
break;
default:
break;
}
}
/**
***********************************************************
* @brief 对数据进行异或运算
* @param data, 存储数组的首地址
* @param len, 要计算的元素的个数
* @return 异或运算结果
***********************************************************
*/
static uint8_t CalXorSum(const uint8_t *data, uint32_t len)
{
uint8_t xorSum = 0;
for (uint32_t i = 0; i < len; i++)
{
xorSum ^= data[i];
}
return xorSum;
}
/**
***********************************************************
* @brief LED控制处理函数
* @param ctrlData,结构体指针,传入LED的编号和状态
* @return
***********************************************************
*/
static void CtrlLed(LedCtrlInfo_t *ctrlData)
{
ctrlData->ledState != 0 ? TurnOnLed(ctrlData->ledNo) : TurnOffLed(ctrlData->ledNo);
}
/**
***********************************************************
* @brief USB转串口任务处理函数
* @param
* @return
***********************************************************
*/
void Usb2ComTask(void)
{
if (!g_pktRcvd)
{
return;
}
g_pktRcvd = false;
if (CalXorSum(g_rcvDataBuf, PACKET_DATA_LEN - 1) != g_rcvDataBuf[PACKET_DATA_LEN - 1])
{
return;
}
if (g_rcvDataBuf[FUNC_DATA_IDX] == LED_CTRL_CODE)
{
CtrlLed((LedCtrlInfo_t *)(&g_rcvDataBuf[FUNC_DATA_IDX + 1]));
}
}
/**
***********************************************************
* @brief USB转串口应用初始化函数
* @param
* @return
***********************************************************
*/
void Usb2ComAppInit(void)
{
regUsb2ComCb(ProcUartData);
}
19.0 main.c
cpp
#include <stdint.h>
#include <stdio.h>
#include "gd32f30x.h"
#include "led_drv.h"
#include "key_drv.h"
#include "systick.h"
#include "usb2com_drv.h"
#include "usb2com_app.h"
#include "delay.h"
static void DrvInit(void)
{
SystickInit();
LedDrvInit();
KeyDrvInit();
DelayInit();
Usb2ComDrvInit();
}
static void AppInit(void)
{
Usb2ComAppInit();
}
int main(void)
{
DrvInit();
AppInit();
while (1)
{
Usb2ComTask();
//DelayNms(1000);
}
}
后记:
注:基于本人回顾理解,仅供学习参考