UART基本定义、三种编程方式、freertos内怎么用、怎么封装

news2024/11/16 20:33:38

文章目录

    • 串口基本概念
    • 串口的三种编程方式
      • uart编程
        • 查询方式不常用、其他两个方式用的多
        • 中断方式:
          • 代码
          • 原理
        • DMA方式:
          • 配置DMA
          • 原理
          • 代码
        • 效率最高的UART编程方式:
          • 是什么?
          • 操作
      • 在freertos里面调用uart
          • 应该怎么做?
          • 代码
      • 面向对象封装UART
        • 串口的DMA设置:
        • 编写代码:
          • 信号量:启动DAM、等待信号量、释放信号量(在回调函数)
          • 怎么封装函数
      • 编写遇到的问题
    • 寄存器
      • 串口通讯不许连续发送,串口为什么一次只发一个字节?
      • 波特率、比特率
      • 通讯协议
      • FIFO

串口基本概念

全双工
低位先行

TXD发、RXD接
起始位 | 数据位 | 校验位 | 停止位
0 8-9位 奇/偶校验 1

通讯前的约定(协议)用串口时双方要协定好没传输一个数据需要多少秒(约定好波特率)
奇偶校验位
数据位+校验位个数位奇数个,则正确
波特率bps,每一秒传输数据的位数

串口的三种编程方式

注意:中断方式和DMA方式
第一个Transmit都是使能中断,然后在中断中完成传输,在中断的最后有一个回调函数callback,callback为_weak函数,用户可以自己去写具体要求
1、查询方式
收/发数据时需要不停查看相应寄存器是否为空
2、中断方式
Transmit_IT使能中断
callback会给反馈,但也是会经常打断cpu
image.png
3、DMA
使用中断方式时,在传输、接收数据时,会发生中断,还需要 CPU 执行中断处理函数。有另外一种方法: DMA(Direct Memory Access),它可以直接在 2 个设备之间传递数据,无需 CPU 参与
DMA就是跑腿的
image.png

uart编程

查询方式不常用、其他两个方式用的多

三种方式,只实现串口2发送、串口4接收;
串口2接收、4发送省去;

中断方式:

收到一个字符就会产生一个中断,就会去中断cpu;DMA是接收完所有字符才产生一次中断
具体实现:
首先要使能中断
image.png

代码
static volatile int g_uart2_tx_complete = 0;//用来判断是否完成
static volatile int g_uart4_rx_complete = 0;

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	//数据返送完毕,中断函数会调用这个回调函数
	if(huart == &huart2)
	{
		g_uart2_tx_complete = 1;//数据发送完后就会置成1,wait看到1则置为0表示完成、如果一直是0直到超时则返回-1表示失败
	}
}

int Wait_UART2_Tx_Complete(int timeout)
{
	while(g_uart2_tx_complete == 0 && timeout)
	{
		vTaskDelay(1);
		timeout--;
	};
	if(timeout == 0)//超时
		return -1;
	else
	{
		g_uart2_tx_complete = 0;
		return 0;
	}
}



void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//数据返送完毕,中断函数会调用这个回调函数
	if(huart == &huart4)
	{
		g_uart4_rx_complete = 1;//数据发送完后就会置成1,wait看到1则置为0表示完成、如果一直是0直到超时则返回-1表示失败
	}
}

int Wait_UART4_Rx_Complete(int timeout)
{
	while(g_uart4_rx_complete == 0 && timeout)
	{
		vTaskDelay(1);
		timeout--;
	}
	if(timeout == 0)//超时
		return -1;
	else
	{
		g_uart4_rx_complete = 0;
		return 0;
	}
}
extern UART_HandleTypeDef huart4;
extern UART_HandleTypeDef huart2;

//发送
int Wait_UART2_Tx_Complete(int timeout);
//接收
int Wait_UART4_Rx_Complete(int timeout);

/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .priority = (osPriority_t) osPriorityNormal,
  .stack_size = 128 * 4
};

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

//任务函数
static void SPILCDTaskFunction( void *pvParameters )
{
	char bur[100];
	int cnt = 0;
	
	while(1)
	{
		sprintf(bur, "lcd task test:%d" ,cnt++);
		//Draw_String(0, 0 , bur, 0x0000ff00, 0);
		vTaskDelay(1000);
	}
	
}

static void CH1_URAT2_TxTaskFunction( void *pvParameters )
{
	uint8_t c = 0;
	while(1)
	{
		//发数据
		HAL_UART_Transmit_IT(&huart2, &c, 1);
		Wait_UART2_Tx_Complete(100);//等待发送完成
		vTaskDelay(500);
		c++;
	}
}
	
static void CH2_URAT4_RxTaskFunction( void *pvParameters )
{
	
	uint8_t c = 0;
	char bur[100];
	int cnt = 0;
	HAL_StatusTypeDef err;
	while(1)
	{
		//接收数据
		err = HAL_UART_Receive_IT(&huart4, &c, 1);//串口、内容地坿、长度㿁超旿
		
		if(Wait_UART4_Rx_Complete(10) == 0)//=0表示接收完成
		{
			sprintf(bur,"receive dataset : 0x:%02x, numember:%d",c, cnt++);
			Draw_String(0, 0, bur, 0x0000ff00, 0);
		}
		else
		{
			HAL_UART_AbortReceive_IT(&huart4);//超时或者出错则调用终止中断接收的函数
		}
		
	}
		
}
原理

image.png
image.png

DMA方式:

在dma传输过程中不产生中断,传输完指定数量的数据后产生中断;
dma只会去中断cpu一次;
优点:DMA优势就在于可以接收很多数据;
image.png
源 | 目的 | 长度
发送:内存的源地址++、TDR
接收:RDR 、目的地址++
image.png

配置DMA

image.png

原理

image.png
image.png

代码

就只是把中断的代码的这些换了
image.png

效率最高的UART编程方式:
是什么?

正常的三种编程方式1启动2等待完成,一般是等到如下图设置的1000个字节都收到后停止,但是其他比如完整的数据收到了没到1000字节,以及长时间未响应、产生error就要用到IDLE中断;
等待完成如果已经收到完整的数据但是没有达到如下如1000个字节,那么就要靠IDLE中断来告知收到完整数据了。

问题:中断和DMA每次都要手工使能中断/启动DMA,如果代码里面有其他长时间的任务没结束,第二次就要等这个任务结束后才启动下一次;
方法:一开始就启动DMA

使用DAM+IDLE中断:
其他方式都可以用IDLE但是DMA是最好的,中断方式没有必要用这个,因为他要及时的获取数字每读到一个字节、就产生一次中断,去中断一次cpu
image.png
空闲而停止mcu检测到长的停止时间,就会产生IDLE中断
image.png

操作

1、一开始就使能IDLE的这个函数
image.png
2、实现回调函数
image.png

回调函数创建队列都是在中断函数中实现的,回调函数就是在中断函数中调用的
中断里面写队列要有一个后缀FromISR

在freertos里面调用uart

image.png
多了freertos队列

应该怎么做?

image.png

代码
static volatile int g_uart2_tx_complete = 0;//用来判断是否完成
static volatile int g_uart4_rx_complete = 0;
static uint8_t g_uart4_rx_buf[100];//定义一个buff来存接收到的数据
static QueueHandle_t g_xUART4_RX_Queue;//创建队列

void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	//数据返鿁完毕,中断函数会调用这个回调函敿
	if(huart == &huart2)
	{
		g_uart2_tx_complete = 1;//数据发鿁完后就会置房1,wait看到1则置丿0表示完成、如果一直是0直到超时则返囿-1表示失败
	}
}

int Wait_UART2_Tx_Complete(int timeout)
{
	while(g_uart2_tx_complete == 0 && timeout)
	{
		vTaskDelay(1);
		timeout--;
	};
	if(timeout == 0)//超时
		return -1;
	else
	{
		g_uart2_tx_complete = 0;
		return 0;
	}
}


//接收完毕
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//数据返鿁完毕,中断函数会调用这个回调函敿
	if(huart == &huart4)
	{
		g_uart4_rx_complete = 1;//数据发鿁完后就会置房1,wait看到1则置丿0表示完成、如果一直是0直到超时则返囿-1表示失败
		
		//收到数据后把收到的数据存入buff,写队列
		for(int i = 0 ; i < 100; i++)
		{
			xQueueSendFromISR(g_xUART4_RX_Queue,&g_uart4_rx_buf[i], NULL);
		}
		
		//重新启动DMA+IDLE
		HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);
	}
}

//void event,接收空闲,表示数据已经接收完成,但是还没到DMA接收设置的值
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	if(huart == &huart4)
	{
		g_uart4_rx_complete = 1;
		//写队列
		for(int i = 0 ; i < Size; i++)
		{
			xQueueSendFromISR(g_xUART4_RX_Queue,&g_uart4_rx_buf[i], NULL);
		}
		
		//重新启动DMA+IDLE
		HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);
		
	}
		
}

//void error:重新启动DMA+IDLE
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
	//重启DMA+IDLE
	HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);
}

int Wait_UART4_Rx_Complete(int timeout)
{
	while(g_uart4_rx_complete == 0 && timeout)
	{
		vTaskDelay(1);
		timeout--;
	}
	if(timeout == 0)//超时
		return -1;
	else
	{
		g_uart4_rx_complete = 0;
		return 0;
	}
}


//读数据,app从队列中读数据不从串口读数据了

int UART4_GetData(uint8_t *pData)
{
	xQueueReceive(g_xUART4_RX_Queue,pData, portMAX_DELAY);
	return 0;
}

void UART4_RX_Start(void)
{
	//开始前把上面定义好的队列创建处来
	 g_xUART4_RX_Queue = xQueueCreate( 200, 1 );
	//启动接收
	 HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);
		//收到的数据保存在哪里,要定义一个buff;收到后回调函数就会被调用
}
extern UART_HandleTypeDef huart4;
extern UART_HandleTypeDef huart2;
void UART4_RX_Start(void);
int UART4_GetData(uint8_t *pData);

//发鿿
int Wait_UART2_Tx_Complete(int timeout);
//接收
int Wait_UART4_Rx_Complete(int timeout);

/* USER CODE END Variables */
/* Definitions for defaultTask */
osThreadId_t defaultTaskHandle;
const osThreadAttr_t defaultTask_attributes = {
  .name = "defaultTask",
  .priority = (osPriority_t) osPriorityNormal,
  .stack_size = 128 * 4
};

/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN FunctionPrototypes */

//任务函数
static void SPILCDTaskFunction( void *pvParameters )
{
	char bur[100];
	int cnt = 0;
	
	while(1)
	{
		sprintf(bur, "lcd task test:%d" ,cnt++);
		//Draw_String(0, 0 , bur, 0x0000ff00, 0);
		vTaskDelay(1000);
	}
	
}

static void CH1_URAT2_TxTaskFunction( void *pvParameters )
{
	uint8_t c = 0;
	while(1)
	{
		//发数捿
		HAL_UART_Transmit_DMA(&huart2, &c, 1);
		Wait_UART2_Tx_Complete(100);//等待发鿁完房
		vTaskDelay(500);
		c++;
	}
}
	
static void CH2_URAT4_RxTaskFunction( void *pvParameters )
{
	
	uint8_t c = 0;
	char bur[100];
	int cnt = 0;
	HAL_StatusTypeDef err;
	while(1)
	{
		//一开头就调用这个函数来调用到IDLE
		UART4_RX_Start();
		
		err = UART4_GetData(&c);//读到的数据保存在c
		if(err == 0)//=0表示接收完成
		{
			sprintf(bur,"receive dataset : 0x:%02x, numember:%d",c, cnt++);
			Draw_String(0, 0, bur, 0x0000ff00, 0);
		}
		else
		{
			HAL_UART_DMAStop(&huart4);//超时或迅出错则调用终止中断接收的函敿
		}
		
	}
		
}

200个数据,每个数据一个字节
image.png

面向对象封装UART

构造处结构体,包含uart里面的初始话函数、构造函数等等;

串口的DMA设置:

前面只设置了uart2发送和uart4接收;
现在设置uart4接收和uart2发送;
源地址叠加和目的地址是否叠加在前面写了;
发送一定是内存到外设,接收则相反
image.pngimage.png

编写代码:

uart接收复制uart4接收,等待、获取数据、启动函数(等待接收函数不需要了删除即可,直接等待队列完成);
callback直接在callback里面复制;
getData设置超时时间;

等待函数去掉,等待队列就行了,换成freertos的信号量:
中断里面不能give互斥量mutex,啥是互斥量?信号量和互斥量
优先级的恢复工作不太好做

信号量:启动DAM、等待信号量、释放信号量(在回调函数)

image.png
过程原理
二进制信号量先定义出来->调用创建信号量函数
send函数发送出去,然后等待中断里面的callback回调函数give,计数值变成1
send函数take拿走这个1;
image.png

怎么封装的?
声明和定义结构体
image.png
把uart里面的这些函数封装起来
image.png

怎么封装函数

把这几个函数放入结构体中
image.png
这个结构体的成员函数如下,这样就能直接定义出这个结构体,用->来初始化、发数据、收数据
image.png
image.png
代码

#include "uart_device.h"
#include <stdio.h>
#include <string.h>


extern struct UART_Device g_uart2_dev;
extern struct UART_Device g_uart4_dev;

static struct UART_Device *g_uart_devices[] = {&g_uart2_dev, &g_uart4_dev};

//根据名字遍历这个指针,返回结构体地址
struct UART_Device * GetUARDevice(char *name)
{
	int i = 0;
	for(i = 0; i < sizeof(g_uart_devices)/sizeof(g_uart_devices[0]); i++)
	{
		if(!strcmp(name, g_uart_devices[i]->name))
			return g_uart_devices[i];
	
	}
	return NULL;
}
#ifndef __UART_DEVICE_H
#define __UART_DEVICE_H

#include <stdint.h>

struct UART_Device {
	char *name;
	int (*Init)( struct UART_Device *pDev, int baud, char parity, int data_bit, int stop_bit);
	int (*Send)( struct UART_Device *pDev, uint8_t *datas, uint32_t len, int timeout);
	int (*RecvByte)( struct UART_Device *pDev, uint8_t *data, int timeout);
};

struct UART_Device *GetUARDevice(char *name);


#endif /* __UART_DEVICE_H */

//任务函数
static void SPILCDTaskFunction( void *pvParameters )
{
	char bur[100];
	int cnt = 0;
	
	while(1)
	{
		sprintf(bur, "lcd task test:%d" ,cnt++);
		//Draw_String(0, 0 , bur, 0x0000ff00, 0);
		vTaskDelay(1000);
	}
	
}

static void CH1_URAT2_TxTaskFunction( void *pvParameters )
{ 
	uint8_t c = 0;
	
	
	struct UART_Device *pdev = GetUARDevice("uart2");
	pdev->Init(pdev , 115200, 'N', 8, 1);
	
	
	
	while(1)
	{
		pdev->Send(pdev, &c, 1, 100);
		vTaskDelay(500);
		c++;
	}
}
	
static void CH2_URAT4_RxTaskFunction( void *pvParameters )
{
	
	uint8_t c = 0;
	char bur[100];
	int cnt = 0;
	int err;
	struct UART_Device *pdev = GetUARDevice("uart4");
	
	pdev->Init(pdev , 115200, 'N', 8, 1);
	
	while(1)
	{

		err = pdev->RecvByte(pdev, &c, 100);
		if(err == 0)//=0表示接收完成
		{
			sprintf(bur,"receive dataset : 0x:%02x, numember:%d",c, cnt++);
			Draw_String(0, 0, bur, 0x0000ff00, 0);
		}
		else
		{
			//HAL_UART_DMAStop(&huart4);//超时或迅出错则调用终止中断接收的函敿
		}
		
	}
		
}


/* USER CODE END FunctionPrototypes */

/**
  * @brief  FreeRTOS initialization
  * @param  None
  * @retval None
  */
void MX_FREERTOS_Init(void) {
  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* USER CODE BEGIN RTOS_MUTEX */
  /* add mutexes, ... */
  /* USER CODE END RTOS_MUTEX */

  /* USER CODE BEGIN RTOS_SEMAPHORES */
  /* add semaphores, ... */
  /* USER CODE END RTOS_SEMAPHORES */

  /* USER CODE BEGIN RTOS_TIMERS */
  /* start timers, add new ones, ... */
  /* USER CODE END RTOS_TIMERS */

  /* USER CODE BEGIN RTOS_QUEUES */
  /* add queues, ... */
  /* USER CODE END RTOS_QUEUES */
  /* creation of defaultTask */
  defaultTaskHandle = osThreadNew(StartDefaultTask, NULL, &defaultTask_attributes);

  /* USER CODE BEGIN RTOS_THREADS */
  /* add threads, ... */
	xTaskCreate(
		SPILCDTaskFunction, // 函数指针, 任务函数
		"spi_lcd_task", // 任务的名孿
		200, // 栈大尿,单位为word,10表示40字节
		NULL, // 调用任务函数时传入的参数
		osPriorityNormal, // 优先线
		NULL ); // 任务句柄, 以后使用它来操作这个任务
		
	xTaskCreate(
		CH1_URAT2_TxTaskFunction, // 函数指针, 任务函数
		"ch1_uart2_tx_task", // 任务的名孿
		200, // 栈大尿,单位为word,10表示40字节
		NULL, // 调用任务函数时传入的参数
		osPriorityNormal, // 优先线
		NULL ); // 任务句柄, 以后使用它来操作这个任务
		
	xTaskCreate(
		CH2_URAT4_RxTaskFunction, // 函数指针, 任务函数
		"ch2_uart4_rx_task", // 任务的名孿
		200, // 栈大尿,单位为word,10表示40字节
		NULL, // 调用任务函数时传入的参数
		osPriorityNormal, // 优先线
		NULL ); // 任务句柄, 以后使用它来操作这个任务
	
  /* USER CODE END RTOS_THREADS */

  /* USER CODE BEGIN RTOS_EVENTS */
  /* add events, ... */
  /* USER CODE END RTOS_EVENTS */

}
static SemaphoreHandle_t g_UART2_TX_Semaphore;
static uint8_t g_uart4_rx_buf[100];//定义丿个buff来存接收到的数据
static QueueHandle_t g_xUART4_RX_Queue;//创建队列

static SemaphoreHandle_t g_UART4_TX_Semaphore;
static uint8_t g_uart2_rx_buf[100];
static QueueHandle_t g_xUART2_RX_Queue;
struct UART_Device;//表示这是一个结构体类型



//send callback
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
	//数据返鿁完毕,中断函数会调用这个回调函敿
	if(huart == &huart2)
	{
		xSemaphoreGiveFromISR(g_UART2_TX_Semaphore, NULL);
	}
	
	
	if(huart == &huart4)
	{
		xSemaphoreGiveFromISR(g_UART4_TX_Semaphore, NULL);
	}
	
}


//receive callback
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	//数据返鿁完毕,中断函数会调用这个回调函敿
	if(huart == &huart4)
	{
		
		//收到数据后把收到的数据存入buff,写队列
		for(int i = 0 ; i < 100; i++)
		{
			xQueueSendFromISR(g_xUART4_RX_Queue,&g_uart4_rx_buf[i], NULL);
		}
		
		//重新启动DMA+IDLE
		HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);
	}
		
		
	if(huart == &huart2)
	{
		
		//收到数据后把收到的数据存入buff,写队列
		for(int i = 0 ; i < 100; i++)
		{
			xQueueSendFromISR(g_xUART2_RX_Queue,&g_uart2_rx_buf[i], NULL);
		}
		
		//重新启动DMA+IDLE
		HAL_UARTEx_ReceiveToIdle_DMA(&huart2, g_uart2_rx_buf, 100);	
	}
}


//receive  void event,接收空闿,表示数据已经接收完成,但是还没到DMA接收设置的忿
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
	if(huart == &huart4)
	{
		//写队刿
		for(int i = 0 ; i < Size; i++)
		{
			xQueueSendFromISR(g_xUART4_RX_Queue,&g_uart4_rx_buf[i], NULL);
		}
		
		//重新启动DMA+IDLE
		HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);	
	}
	
	if(huart == &huart2)
	{
		//写队刿
		for(int i = 0 ; i < Size; i++)
		{
			xQueueSendFromISR(g_xUART2_RX_Queue,&g_uart2_rx_buf[i], NULL);
		}
		
		//重新启动DMA+IDLE
		HAL_UARTEx_ReceiveToIdle_DMA(&huart2, g_uart2_rx_buf, 100);	
	}
}

//receive void error:重新启动DMA+IDLE
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{
	//重启DMA+IDLE
	if(huart == &huart4)
	{
		HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);
	}

	
	if(huart == &huart2)
	{
		HAL_UARTEx_ReceiveToIdle_DMA(&huart2, g_uart2_rx_buf, 100);
	}

}




//读数据,app从队列中读数据不从串口读数据亿



/**************/
/**************/



/*  uart4接收、uart2发送  */
int UART2_Send(struct UART_Device *pDev, uint8_t *datas,uint32_t len, int timeout)
{
		HAL_UART_Transmit_DMA(&huart2, datas, len);
		//wait Semaphore 信号量
		if(pdTRUE == xSemaphoreTake(g_UART2_TX_Semaphore,timeout))
			return 0;
		else
			return -1;
}




int UART4_GetData(struct UART_Device *pDev,uint8_t *pData, int timeout)
{
	if(pdPASS == xQueueReceive(g_xUART4_RX_Queue,pData, timeout))
		return 0;
	else 
		return -1;
}

int UART4_RX_Start(struct UART_Device *pDev, int baud, char parity, int data_bit, int stop_bit)
{
	//弿始前把上面定义好的队列创建处板
	if(!g_xUART2_RX_Queue) 
	{
		g_xUART4_RX_Queue = xQueueCreate( 200, 1 );
		//启动接收
		HAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);
		//收到的数据保存在哪里,要定义1个buff;收到后回调函数就会被调用
	
		//创建信号量
		g_UART4_TX_Semaphore = xSemaphoreCreateBinary();
	}
	return 0;
}



/*****************/
/*  uart2接收、uart4发送  */
int UART2_GetData(struct UART_Device *pDev, uint8_t *pData, int timeout)
{
	if(pdPASS == xQueueReceive(g_xUART2_RX_Queue,pData, timeout))
		return 0;
	else 
		return -1;
}

int UART2_RX_Start(struct UART_Device *pDev, int baud, char parity, int data_bit, int stop_bit)
{
	if (!g_xUART2_RX_Queue)
	{
		g_xUART2_RX_Queue = xQueueCreate(200, 1);
		g_UART2_TX_Semaphore = xSemaphoreCreateBinary();
		HAL_UARTEx_ReceiveToIdle_DMA(&huart2, g_uart2_rx_buf, 100);
	}
	return 0;
}

int UART4_Send(struct UART_Device *pDev, uint8_t *datas,uint32_t len, int timeout)
{
		HAL_UART_Transmit_DMA(&huart4, datas, len);
		//wait Semaphore 信号量
		if(pdTRUE == xSemaphoreTake(g_UART4_TX_Semaphore,timeout))
			return 0;
		else
			return -1;
}




struct UART_Device g_uart2_dev = {"uart2", UART2_RX_Start, UART2_Send, UART2_GetData};
struct UART_Device g_uart4_dev = {"uart4", UART4_RX_Start, UART4_Send, UART4_GetData};

编写遇到的问题

1、./Core/Src/usart.c(377): warning: passing ‘volatile uint8_t [100]’ to parameter of type ‘uint8_t *’ (aka ‘unsigned char *’) discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
image.png
image.png
image.png

2、undefined symbol
把static去掉后就好了
image.png
image.png
另一个文件
image.png

寄存器

串口通讯不许连续发送,串口为什么一次只发一个字节?

1、避免累计误差;
2、串口通讯是异步发送,就是发送方和接受方有各自的时钟,时钟不同步,时钟同步的话可以发好多个字节;

波特率、比特率

波特率表示每秒传输信号的状态数,如果一个波形传输一个bit,那就=bit率,每秒传输的二进制位
一个波形传输n个比特
波特率= n比特率
image.png
总之:
波特率: 1 秒内传输信号的状态数(波形数)。比特率: 1 秒内传输数据的 bit数。如果一个波形,能表示 N 个 bit,那么:波特率 * N = 比特率。

通讯协议

并行8根线一次发8位
image.png
串行通信一根线发
image.png

单工,只能单向
双工双向,半双工一条通道接受和发送不能同时工作
全双工两个通道可以,同时收发
image.png

FIFO

FIFO(First In First Out,即先入先出),是一种数据缓冲器。先被写入的数据会按顺序先被读出。FIFO可看做一个管道,有数据写入端口和 数据读取端口:

设置异步通信
image.png

设置数据位,校验位、波特率、停止位
image.png

memset
image.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1809386.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

每日复盘-20240607

今日关注&#xff1a; 这几天市场环境不好&#xff0c;一直空仓。 六日涨幅最大: ------1--------605258--------- 协和电子 五日涨幅最大: ------1--------605258--------- 协和电子 四日涨幅最大: ------1--------605258--------- 协和电子 三日涨幅最大: ------1--------0…

在Linux or Windows中如何优雅的写出对拍

在Linux or Windows中如何优雅的写出对拍 一、前言二、结论1、对拍 三、对拍详解1、什么是对拍呢&#xff1f;&#x1f9d0;2、对拍的组成部分3、输入数据生成4、对拍程序5、操作流程 四、最后 一、前言 网上的对拍程序层出不穷&#xff0c;大多Linux和Windows中的对拍程序都是…

已解决Error || KeyError: ‘The truth value of a Series is ambiguous‘

已解决Error || KeyError: ‘The truth value of a Series is ambiguous’ &#x1f680; 原创作者&#xff1a; 猫头虎 作者微信号&#xff1a; Libin9iOak 作者公众号&#xff1a; 猫头虎技术团队 更新日期&#xff1a; 2024年6月6日 博主猫头虎的技术世界 &#x1f3…

Python 连接 MySQL 及 SQL增删改查(主要使用sqlalchemy)

目录 一、环境 二、MySQL的连接和使用 2.1方式一&#xff1a;sql为主 2.1.1创建连接 2.1.2 表结构 2.1.3 新增数据 ​编辑 2.1.4 查看数据 ​编辑 2.1.5 修改数据 2.1.6 删除数据 2.2方式二&#xff1a;orm对象关系映射 2.2.1 mysql连接 2.2.2 创建表 2.2.3 新增…

已解决Error || IndexError: index 3 is out of bounds for axis 0 with size 3

已解决Error || IndexError: index 3 is out of bounds for axis 0 with size 3 原创作者&#xff1a; 猫头虎 作者微信号&#xff1a; Libin9iOak 作者公众号&#xff1a; 猫头虎技术团队 更新日期&#xff1a; 2024年6月6日 博主猫头虎的技术世界 &#x1f31f; 欢迎来…

【Linux文件篇】优化文件读写,加速数据处理策略——缓冲区

W...Y的主页 &#x1f60a; 代码仓库分享 &#x1f495; 前言&#xff1a;我们已经复习了C语言中的接口&#xff0c;并且学习了许多文件系统调用&#xff0c;了解了文件描述符以及重定向。今天我们继续学习文件缓冲区的相关内容。 缓冲区 在学习C语言时&#xff0c;我们经常…

QSlider样式示例

参考代码&#xff1a; /********************QSlider横向滑动条样式**********************/ QSlider {background-color: rgba(170, 255, 255, 100); /* 设置滑动条主体*/ }QSlider::groove:horizontal {border: 1px solid #999999;height: 8px; /* 默认…

风能远程管理ARMxy嵌入式系统深度解析

智能技术正以前所未有的速度融入传统能源管理体系&#xff0c;而ARMxy工业计算机作为这一变革中的关键技术载体&#xff0c;正以其独特的性能优势&#xff0c;为能源管理的智能化升级铺设道路。本文将聚焦于智能电表、太阳能电站监控、风力发电站远程管理三大应用场景&#xff…

代码随想录算法训练营第三十一天| 455.分发饼干,376. 摆动序列 ,53. 最大子序和

455. 分发饼干 - 力扣&#xff08;LeetCode&#xff09; class Solution {public int findContentChildren(int[] g, int[] s) {Arrays.sort(g); //递增Arrays.sort(s); int result 0;//遍历&#xff0c;先满足小的int i0,j0;for(;i<g.length && j<s.length;i){…

Adobe Premiere Pro 2024下载安装(视频剪辑软件Pr2024)

百度网盘下载地址&#xff08;含PR教学课程&#xff08;PR从入门到精通108节课程&#xff09;&#xff09;https://pan.baidu.com/s/1WKYZENoMzTcKhbgMgbEPGQ?pwdSIMS 一、Pr简介 Pr全称Premiere&#xff0c;是Adobe公司开发的一款功能强大的视频剪辑软件&#xff0c;目前被…

AI作画工具介绍

目录 1.概述 2.Stable Diffusion 2.1.诞生背景 2.2.版本历史 2.3.优点 2.4.缺点 2.5.应用场景 2.6.未来展望 3.Midjourney 3.1.诞生背景 3.2.版本历史 3.3.优点 3.4.缺点 3.5.应用场景 3.6.未来展望 4.总结 1.概述 AI作画工具是一种运用人工智能技术&#xff…

pxe自动装机:

pxe自动装机&#xff1a; 服务端和客户端 pxe c/s模式&#xff0c;允许客户端通过网络从远程服务器&#xff08;服务端&#xff09;下载引导镜像&#xff0c;加载安装文件&#xff0c;实现自动化安装操作系统。 无人值守 无人值守&#xff0c;就是安装选项不需要人为干预&am…

【文末附gpt升级秘笈】AI热潮降温与AGI场景普及的局限性

AI热潮降温与AGI场景普及的局限性 摘要&#xff1a; 随着人工智能&#xff08;AI&#xff09;技术的迅猛发展&#xff0c;AI热一度席卷全球&#xff0c;引发了广泛的关注和讨论。然而&#xff0c;近期一些学者和行业专家对AI的发展前景提出了质疑&#xff0c;认为AI热潮将逐渐…

CentOS6.10升级OpenSSH

openssh下载地址&#xff1a; pub-OpenBSD-OpenSSH-portable安装包下载_开源镜像站-阿里云 openssl下载地址&#xff1a; [ Downloads ] - /source/index.html 1、安装依赖 yum -y install gcc gcc-c yum -y install zlib yum -y install zlib-devel 也可以一起安装。但是…

深度学习中embedding层的理解

Embedding层作用 在深度学习领域中&#xff0c;Embedding层扮演着至关重要的角色&#xff0c;尤其在处理文本数据或类别数据。Embedding层的功能有两个&#xff1a; 1. 将高维稀疏的输入数据&#xff08;如单词、类别标签等&#xff09;转换为低维稠密的向量表示&#xff0c;…

【EAI】生成可爱的贴纸

贴纸生成工具上线啦&#xff0c;目前支持贴纸生成、文生图功能。 地址&#xff1a;https://eai.coderbox.cn/ 功能&#xff1a; 贴纸生成 通过简单提示词&#xff0c;生成可爱的贴纸&#xff0c;支持4种像素规格文生图 基于开源模型实现&#xff0c;模型持续集成中作品库 生…

管理数据必备;侦听器watch用法详解,vue2与vue3中watch的变化与差异

目录 一、侦听器&#xff08;watch&#xff09;是什么&#xff1f; 二、Vue2中的watch&#xff08;Options API&#xff09; 2.1、函数式写法 2.2、对象式写法 ①对象式基础写法 ②回调函数handler ③deep属性 ④immediate属性 三、Vue3中的watch 3.1、向下兼容&#xff…

打造你的博客帝国:DjangoBlog带你飞向国际舞台!

你的网站加载速度还在慢如蜗牛吗&#xff1f;将为你揭开网站速度提升的神秘面纱。从缓存策略到数据库优化&#xff0c;再到高效的代码实践&#xff0c;我们深入探讨了如何让DjangoBlog飞速运行。不仅如此&#xff0c;我们还提供了实用的监控和日志管理技巧&#xff0c;确保你的…

⌈ 传知代码 ⌋ Flan-T5 使用指南

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

软件游戏steam_api.dll丢失的解决方法,总结5种有效的方法

在玩电脑游戏时&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是“游戏缺少steam_api.dll”。这个问题可能让很多玩家感到困惑和烦恼。那么&#xff0c;究竟是什么原因导致游戏缺少steam_api.dll呢&#xff1f;又该如何解决这个问题呢&#xff1f;本文将为大家…