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

文章目录

    • 串口基本概念
    • 串口的三种编程方式
      • 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+IDLEHAL_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+IDLEHAL_UARTEx_ReceiveToIdle_DMA(&huart4, g_uart4_rx_buf, 100);}}//void error:重新启动DMA+IDLE
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{//重启DMA+IDLEHAL_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){//一开头就调用这个函数来调用到IDLEUART4_RX_Start();err = UART4_GetData(&c);//读到的数据保存在cif(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+IDLEHAL_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+IDLEHAL_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+IDLEHAL_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+IDLEHAL_UARTEx_ReceiveToIdle_DMA(&huart2, g_uart2_rx_buf, 100);	}
}//receive void error:重新启动DMA+IDLE
void HAL_UART_ErrorCallback(UART_HandleTypeDef *huart)
{//重启DMA+IDLEif(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;elsereturn -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;elsereturn -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.mzph.cn/diannao/29540.shtml

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

相关文章

uniapp canvas生成海报

效果 封装组件&#xff0c;父组件 ref 调用 downImgUrl()函数&#xff0c;其他根据自己需求改 <template><view><view class"bgpart"><canvas class"canvas-wrap" canvas-id"canvasID" type"2d"></canvas…

jsp运行提示_jsp.java某行存在错误问题的解决

jsp运行提示XXX_jsp.java某行存在错误问题的解决 在编译运行jsp文件时&#xff0c;出现类似如下提示&#xff1a; 49行发生错误&#xff0c;要注意&#xff1a; 这里所指的49行并非jsp文件的第49行&#xff0c;而是编译后的jsp.java文件的第49行。 因此&#xff1a;解决问题…

saas考试系统和私有化考试系统的区别

SaaS考试系统和私有化考试系统&#xff0c;作为现代信息化教育的重要工具&#xff0c;各有其独特的优势和特点。本文将从多个维度对这两种系统进行深入剖析&#xff0c;以揭示它们之间的核心差异。 首先&#xff0c;从部署方式来看&#xff0c;SaaS考试系统是基于云服务的在线考…

STM32--IAP程序升级实验

1. STM32程序升级方法 1.1 ST-link / J-link下载 将编译生成的hex文件使用ST-Link/J-Link工具直接下载进 Flash 即可。Keil中点击下载也能一键下载。下载后的代码会存放在Flash的起始地址0x0800 0000处。 简单补充一句&#xff0c;bin文件和hex文件的区别&#xff1a; bin文…

论坛产品选型,需要关注哪些点?

论坛社区是一个经久不衰的行业&#xff0c;比如我们常见的宠物社区&#xff0c;校园社区&#xff0c;游戏社区、企业内部社区&#xff0c;品牌社区&#xff0c;本地同城、私域社区项目、付费社群、问答社区等等&#xff0c;可以说是覆盖了各行各业&#xff0c;那么如果我们要搭…

Python实现管线建模 - 3.同心变径管

往期回顾 Python实现管线建模 || 1.圆直管、方管https://blog.csdn.net/Xxy9426/article/details/138836778?spm1001.2014.3001.5501 对依赖库的补充 随着后续内容的深入&#xff0c;我发现单纯靠trimesh库已经无法完成后续的建模&#xff08;涉及到多个几何体拼接或者是创建…

全域外卖系统源码在哪搭建?哪家公司的系统源码最值得推荐

随着全域外卖的兴起&#xff0c;全域外卖系统源码搭建逐渐在众多全域外卖赛道的入局方式中脱颖而出。但是&#xff0c;就目前多个创业者社群就这一话题的讨论程度来看&#xff0c;绝大多数创业者对于全域外卖系统源码搭建模式的了解都仅仅停留在“是什么”层面&#xff0c;而对…

Conmi的正确答案——Vue默认加载方式设置为Yarn后怎么修改

Vue版本&#xff1a;3 1和2主要是搜索文件所在位置&#xff0c;Windows的这个文件一般在“C:\User{当前用户}”下&#xff0c;linux的非root情况下一般在“/home/{当前用户}”下。 1、打开“Everything”&#xff1b; 2、搜索“vuerc”&#xff1b; 3、打开“.vuerc”&#xf…

Boom3D软件下载-Boom3D音效增强工具下载附加详细安装步骤

Boom3D音效增强工具是一款便捷的为用户们进行音频处理和编辑的软件&#xff0c;支持用户们轻松的进行音频的使用&#xff0c;支持超多的音频格式让你可以轻松的进行使用&#xff0c;Boom3D音效增强工具拥有多种音频特效的功能&#xff0c;让你可以在Boom3D音效增强工具轻松的进…

Linux时间子系统5:timekeeper、timecountercyclecounter

1. 前言 前面我们介绍了用户态获取时间的接口clock_gettime&#xff0c;时钟的种类posix_clocks以及时钟源clocksource。那么我们思考这样一个问题&#xff0c;无论clock_gettime或者posix_clock定义的时间都是相对于某个起始点的时间&#xff0c;即相对于Linux Epoch的秒数&am…

pytorch安装----CPU版本

在安装之前&#xff0c;需要先配置GPU环境&#xff08;安装CUDA和CudaNN) 命令行输入nvidia-smi&#xff0c;查看驱动信息 安装相应的CUDA 和CUDANN 验证&#xff1a;输入nvcc --version 或者nvcc -V 进行检查 在anaconda里创建环境 conda create -n py39gpu python3.9激活环…

丰臣秀吉-读书笔记二

“啊&#xff01;平凡的一生&#xff0c;想来也够长了。不过也短。究竟长还是短&#xff1f;或许只有这一刹那是永恒的吧&#xff0c;死亡的一刹那。生命若是永恒&#xff0c;也就取决于那一刹那。” 如果我死了&#xff0c;我的愿望只有这个&#xff1a; “无论多么黑暗&#…

【STC8A8K64D4开发板】第3-1讲:温度传感器DS18B20

第3-1讲&#xff1a;温度传感器DS18B20 学习目的了解DS18B20数字温度传感器的基本原理及其数据格式。掌握STC8A8K64D4与DS18B20单总线通信的程序设计&#xff0c;通信步骤&#xff0c;数据校验等。 硬件电路设计 DS18B20简介 DS18B20主要特性 DS18B20是Dallas 半导体公司推出的…

KernelFuzzer部署、使用与原理分析

文章目录 前言1、概述1.1、整体架构1.2、工作流程1.2.1、环境配置流程1.2.2、计划任务执行流程1.2.3、Fuzz测试流程1.2.3.1、整体资源调度1.2.3.2、选取Fuzz测试目标1.2.3.3、生成Fuzz测试参数1.2.3.4、进行Fuzz测试 2、安装与使用2.1、源码安装2.1.1、部署系统依赖组件2.1.1.1…

硫碳复合材料可用作固态电池正极材料 锂硫电池是重要下游

硫碳复合材料可用作固态电池正极材料 锂硫电池是重要下游 硫碳复合材料&#xff0c;是半固态电池、固态电池的正极材料&#xff0c;主要用于金属硫电池制造领域&#xff0c;在锂硫电池应用中研究热度最高。 锂硫电池&#xff0c;一种二次电池&#xff0c;以硫元素为正极&#x…

HarmonyOS 页面路由(Router)

1. HarmonyOS页面路由(Router) 页面路由指在应用程序中实现不同页面之间的跳转和数据传递。HarmonyOS提供了Router模块&#xff0c;通过不同的url地址&#xff0c;可以方便地进行页面路由&#xff0c;轻松地访问不同的页面。本文将从页面跳转、页面返回和页面返回前增加一个询问…

VBA学习(9):按指定名单一键删除工作表

今天继续给大家聊VBA编程中工作表对象的常用操作&#xff0c;主要内容是如何批量删除工作表&#xff1b;也就是删除单个工作表、删除全部工作表和删除指定名单内的工作表。 1.删除单个工作表 删除工作表需要使用到工作表对象的delete方法&#xff0c;语法格式如下&#xff1a…

聚类分析 #数据挖掘 #Python

聚类分析&#xff08;Cluster Analysis&#xff09;是一种无监督机器学习方法&#xff0c;主要用于数据挖掘和数据分析中&#xff0c;它的目标是将一组对象或观测值根据它们之间的相似性或相关性自动分组&#xff0c;形成不同的簇或类别。聚类分析并不预先知道每个观测值的具体…

可燃气体报警器:户外工地安全预警先锋,定期检定保障安全无忧

在现代化的建设进程中&#xff0c;户外工地作为城市发展的重要推动力&#xff0c;其安全问题一直备受关注。 工地现场往往涉及多种易燃易爆气体&#xff0c;一旦发生泄漏&#xff0c;后果不堪设想。因此&#xff0c;如何有效预警并防范可燃气体泄露&#xff0c;成为户外工地安…

新手小白从Windows转Linux,或许manjaro更适合你!

网管小贾 / sysadm.cc 野生动物园里有一块并不怎么大的水塘&#xff0c;一群火烈鸟就生活在这里。 它们在水塘里悠闲地漫步&#xff0c;饿了就找些小鱼小虾&#xff0c;困了就伸个懒腰、打个盹。 就这样日复一日&#xff0c;过着百无聊赖的日子&#xff0c;直到有一天…… 这…