GD 32串口接收数据

1.0 串口接收数据的几种场景

 MCU 只发送命令接收响应

 串口接收数据比较的复杂,我们可以给数据包定义一定的格式,数据按照固定的格式进行数据的发送和接收。

1:数据的接收端不知道什么时候会接收到数据

2:数据包的长度是不确定长度的

3:接收到的数据频率很快,有时候来不及处理,引入问题


2.0 串口接收数据实验

收发双方需要协商出一些数据格式

帧头:表示一包数据开始了

数据域的长度:也就是包含的数据,后面有几个数据

功能字:0x06 使用8421包含的含义就是 0x0000 0101

LED编号:00/01/02

开关的状态: 0x00 表示关闭,0x01表示开启

校验:异或校验,和校验,奇数偶数校验

一包数据的格式协商出来

数据帧头:表示数据开始了,有时可以使用两个数据来表示数据帧头

数据域的长度:0x03 表示后面的数据是三个数据帧

功能字: 控制外设发出的动作

LED编号:第几个led灯

开关状态:0 表示的是关,1 表示的是开

校验:有多种的校验方式

数据帧头:一般使用特殊的数值【0x55|0xaa】,最好不要出现在有效的数据域内,后面的是数据的长度03表示后面的几个字节,功能字,编号,开关状态,与校验位。


串口数据的接收需要配合中断的方式进行使用

3.0 初始化串口与GPIO

初始化串口与GPIO,设置波特率为115200

/**
***********************************************************
* @brief USB转串口硬件初始化
* @param
* @return 
***********************************************************
*/
void Usb2ComDrvInit(void)
{
	Usb2ComGpioInit();
	Usb2ComUartInit(115200);
}

#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include "gd32f30x.h"


typedef struct
{
	uint32_t uartNo;
	rcu_periph_enum rcuUart;
	rcu_periph_enum rcuGpio;
	uint32_t gpio;
	uint32_t txPin;
	uint32_t rxPin;
	uint8_t irq;
} UartHwInfo_t;

static UartHwInfo_t g_uartHwInfo = {USART0, RCU_USART0, RCU_GPIOA, GPIOA, GPIO_PIN_9, GPIO_PIN_10, USART0_IRQn};

static void Usb2ComGpioInit(void)
{
	rcu_periph_clock_enable(g_uartHwInfo.rcuGpio);
	gpio_init(g_uartHwInfo.gpio, GPIO_MODE_AF_PP, GPIO_OSPEED_10MHZ, g_uartHwInfo.txPin);
	gpio_init(g_uartHwInfo.gpio, GPIO_MODE_IPU, GPIO_OSPEED_10MHZ, g_uartHwInfo.rxPin);
}

static void Usb2ComUartInit(uint32_t baudRate)
{
	/* 使能UART时钟;*/
	rcu_periph_clock_enable(g_uartHwInfo.rcuUart);
	/* 复位UART;*/
	usart_deinit (g_uartHwInfo.uartNo);
	/* 通过USART_CTL0寄存器的WL设置字长;*/ 
	//usart_word_length_set(g_uartHwInfo.uartNo, USART_WL_8BIT);
	/* 通过USART_CTL0寄存器的PCEN设置校验位;*/ 
	//usart_parity_config(g_uartHwInfo.uartNo, USART_PM_NONE);
	/* 在USART_CTL1寄存器中写STB[1:0]位来设置停止位的长度;*/ 
	//usart_stop_bit_set(g_uartHwInfo.uartNo, USART_STB_1BIT);
	/* 在USART_BAUD寄存器中设置波特率;*/ 
	usart_baudrate_set(g_uartHwInfo.uartNo, baudRate);
	/* 在USART_CTL0寄存器中设置TEN位,使能发送功能;*/
	usart_transmit_config(g_uartHwInfo.uartNo, USART_TRANSMIT_ENABLE);
	/* 在USART_CTL0寄存器中设置TEN位,使能接收功能;*/
	usart_receive_config(g_uartHwInfo.uartNo, USART_RECEIVE_ENABLE);
	/* 使能串口接收中断;*/
	usart_interrupt_enable(g_uartHwInfo.uartNo, USART_INT_RBNE);
	/* 使能串口中断;*/
	nvic_irq_enable(g_uartHwInfo.irq, 0, 0);
	/* 在USART_CTL0寄存器中置位UEN位,使能UART;*/ 
	usart_enable(g_uartHwInfo.uartNo);
}





/**
***********************************************************
* @brief USB转串口硬件初始化
* @param
* @return 
***********************************************************
*/
void Usb2ComDrvInit(void)
{
	Usb2ComGpioInit();
	Usb2ComUartInit(115200);
}

4.0 串口中断服务函数

/**
***********************************************************
* @brief 串口0中断服务函数
* @param
* @return 
***********************************************************
*/
void USART0_IRQHandler(void)
{
	if (usart_interrupt_flag_get(g_uartHwInfo.uartNo, USART_INT_FLAG_RBNE) != RESET)
	{
		usart_interrupt_flag_clear(g_uartHwInfo.uartNo, USART_INT_FLAG_RBNE);
		uint8_t uData = (uint8_t)usart_data_receive(g_uartHwInfo.uartNo);
		pProcUartData(uData);
	}
}

判断串口的中断标志位,看串口输入数据寄存器中是否有只,如果串口输入数据寄存器中存在数字将数据发送输出去,调用pProcUartData(),这里下层调用上层的代码使用回调函数的方式间接的进行调用。

// 使用函数指针简介的调用上层的接口函数
static void (*pProcUartData)(uint8_t data);

// 创建注册回调函数的api
void regUsb2ComCb(void (*pFunc)(uint8_t data))
{
	// 使用静态全局变量保存回调函数的地址
	pProcUartData = pFunc;
}

5.0 输出重定向函数printf实现串口打印输出printf

/**
***********************************************************
* @brief printf函数默认打印输出到显示器,如果要输出到串口,
		 必须重新实现fputc函数,将输出指向串口,称为重定向
* @param
* @return 
***********************************************************
*/

int fputc(int ch, FILE *f)
{
	usart_data_transmit(g_uartHwInfo.uartNo, (uint8_t)ch);
	while (RESET == usart_flag_get(g_uartHwInfo.uartNo, USART_FLAG_TBE));
	return ch;
}

 驱动部分完整代码

#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include "gd32f30x.h"


typedef struct
{
	uint32_t uartNo;
	rcu_periph_enum rcuUart;
	rcu_periph_enum rcuGpio;
	uint32_t gpio;
	uint32_t txPin;
	uint32_t rxPin;
	uint8_t irq;
} UartHwInfo_t;

static UartHwInfo_t g_uartHwInfo = {USART0, RCU_USART0, RCU_GPIOA, GPIOA, GPIO_PIN_9, GPIO_PIN_10, USART0_IRQn};

static void Usb2ComGpioInit(void)
{
	rcu_periph_clock_enable(g_uartHwInfo.rcuGpio);
	gpio_init(g_uartHwInfo.gpio, GPIO_MODE_AF_PP, GPIO_OSPEED_10MHZ, g_uartHwInfo.txPin);
	gpio_init(g_uartHwInfo.gpio, GPIO_MODE_IPU, GPIO_OSPEED_10MHZ, g_uartHwInfo.rxPin);
}

static void Usb2ComUartInit(uint32_t baudRate)
{
	/* 使能UART时钟;*/
	rcu_periph_clock_enable(g_uartHwInfo.rcuUart);
	/* 复位UART;*/
	usart_deinit (g_uartHwInfo.uartNo);
	/* 通过USART_CTL0寄存器的WL设置字长;*/ 
	//usart_word_length_set(g_uartHwInfo.uartNo, USART_WL_8BIT);
	/* 通过USART_CTL0寄存器的PCEN设置校验位;*/ 
	//usart_parity_config(g_uartHwInfo.uartNo, USART_PM_NONE);
	/* 在USART_CTL1寄存器中写STB[1:0]位来设置停止位的长度;*/ 
	//usart_stop_bit_set(g_uartHwInfo.uartNo, USART_STB_1BIT);
	/* 在USART_BAUD寄存器中设置波特率;*/ 
	usart_baudrate_set(g_uartHwInfo.uartNo, baudRate);
	/* 在USART_CTL0寄存器中设置TEN位,使能发送功能;*/
	usart_transmit_config(g_uartHwInfo.uartNo, USART_TRANSMIT_ENABLE);
	/* 在USART_CTL0寄存器中设置TEN位,使能接收功能;*/
	usart_receive_config(g_uartHwInfo.uartNo, USART_RECEIVE_ENABLE);
	/* 使能串口接收中断;*/
	usart_interrupt_enable(g_uartHwInfo.uartNo, USART_INT_RBNE);
	/* 使能串口中断;*/
	nvic_irq_enable(g_uartHwInfo.irq, 0, 0);
	/* 在USART_CTL0寄存器中置位UEN位,使能UART;*/ 
	usart_enable(g_uartHwInfo.uartNo);
}





/**
***********************************************************
* @brief USB转串口硬件初始化
* @param
* @return 
***********************************************************
*/
void Usb2ComDrvInit(void)
{
	Usb2ComGpioInit();
	Usb2ComUartInit(115200);
}

// 使用函数指针简介的调用上层的接口函数
static void (*pProcUartData)(uint8_t data);

// 创建注册回调函数的api
void regUsb2ComCb(void (*pFunc)(uint8_t data))
{
	// 使用静态全局变量保存回调函数的地址
	pProcUartData = pFunc;
}


/**
***********************************************************
* @brief 串口0中断服务函数
* @param
* @return 
***********************************************************
*/
void USART0_IRQHandler(void)
{
	if (usart_interrupt_flag_get(g_uartHwInfo.uartNo, USART_INT_FLAG_RBNE) != RESET)
	{
		usart_interrupt_flag_clear(g_uartHwInfo.uartNo, USART_INT_FLAG_RBNE);
		uint8_t uData = (uint8_t)usart_data_receive(g_uartHwInfo.uartNo);
		pProcUartData(uData);
	}
}

/**
***********************************************************
* @brief printf函数默认打印输出到显示器,如果要输出到串口,
		 必须重新实现fputc函数,将输出指向串口,称为重定向
* @param
* @return 
***********************************************************
*/

int fputc(int ch, FILE *f)
{
	usart_data_transmit(g_uartHwInfo.uartNo, (uint8_t)ch);
	while (RESET == usart_flag_get(g_uartHwInfo.uartNo, USART_FLAG_TBE));
	return ch;
}

 5.0 应用层代码实现

#include <stdint.h>
#include <stdio.h>
#include <stdbool.h>
#include "usb2com_drv.h"
#include "led_drv.h"


#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]));
	}
}

// 
void Usb2ComAppInit(void)
{
	regUsb2ComCb(ProcUartData);

}


应用层头文件代码

#ifndef  __USB2COM_APP_H_
#define  __USB2COM_APP_H_

// 串口应用初始化函数
void Usb2ComAppInit(void);
// 串口任务处理函数
void Usb2ComTask(void);

#endif

main 函数代码测试

#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"

static void DrvInit(void)
{
	SystickInit();
	LedDrvInit();
	KeyDrvInit();
	Usb2ComDrvInit();
}

static void AppInit(void)
{
	 Usb2ComAppInit();
}

int main(void)
{	
	DrvInit();
	AppInit();
	while (1)
	{
		Usb2ComTask();
	}
}

相关推荐

  1. gd32F303串口接收的几种方式

    2024-07-20 13:40:02       45 阅读
  2. STM32F1 DMA串口接收数据

    2024-07-20 13:40:02       59 阅读
  3. stm32_HAL_串口不定长数据接收发送

    2024-07-20 13:40:02       28 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-20 13:40:02       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-20 13:40:02       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-20 13:40:02       45 阅读
  4. Python语言-面向对象

    2024-07-20 13:40:02       55 阅读

热门阅读

  1. 082、Python 读文本文件

    2024-07-20 13:40:02       19 阅读
  2. Linux绑定硬件IRQ到指定CPU核

    2024-07-20 13:40:02       15 阅读
  3. 使用内网穿透工具 frp 发布内网 web 站点

    2024-07-20 13:40:02       21 阅读
  4. 【WebRTC】Duplex通信是什么意思?

    2024-07-20 13:40:02       21 阅读
  5. TCP Socket编程示例

    2024-07-20 13:40:02       18 阅读
  6. windows上安装Apache

    2024-07-20 13:40:02       17 阅读
  7. 信息查询_社工

    2024-07-20 13:40:02       15 阅读
  8. Clickhouse 物化视图-optimize无效

    2024-07-20 13:40:02       14 阅读
  9. 07.16_111期_linux_网络通信

    2024-07-20 13:40:02       15 阅读