STM32:USB 虛擬串口以及使用usb->dfu進行iap

本文介紹stm32上usb的常用功能虛擬串口和DFU(Download Firmware Update)

文章目錄

  • 前言
  • 一、usb
  • 二、虛擬串口
    • 1.cubemx配置
        • 1.我們選用高速usb,然后選擇內部低速的phy,這樣使用的usb,最高速度為12Mbit每秒。
        • 2.USB_DEVICE cdc類配置
        • 3.時鐘配置,USB需要使能外設時鐘,且好像只能倍頻為48M
        • 4.最小的堆棧大小也設置的大一點,不然端口識別會出現錯誤。
        • 5.發送和接收函數
    • 2.使用usb的dfu實現iap
      • 1.cubemx
    • 3.代碼
  • tips


前言

USB虛擬串口不僅簡化了硬件設計,避免了傳統串口電平轉換芯片的使用,還能提供更穩定的通信速率和更遠的傳輸距離。對于調試信息輸出、數據采集與監控等應用場景來說,USB虛擬串口是一種非常實用的技術方案。此外,通過USB實現DFU功能,使得用戶可以在不依賴額外硬件工具的情況下,方便快捷地對STM32設備進行固件更新,極大地提高了產品的可維護性和用戶體驗。


提示:以下是本篇文章正文內容,下面案例可供參考

一、usb

關于usb可以看這篇文章
STM32-USB學習系列(一) :USB與USB庫的介紹

我們需要用到的USB的CDC(Communications Device Class)是一種用于通過USB接口實現設備間通信的協議。它旨在為各種類型的通信接口提供標準化的方法,包括但不限于虛擬串口、調制解調器等。CDC通常被用來讓計算機與外部設備進行數據交換,例如連接到計算機的手機、網絡攝像頭或其他外圍設備。

CDC類可以進一步分為多個子類,其中最常見的是抽象控制模型(Abstract Control Model, ACM),這通常用于模擬傳統的串行端口通信。這對于需要通過串行通信來傳輸數據的設備特別有用,比如一些物聯網設備、嵌入式系統或者需要固件更新的硬件。

使用CDC的一個主要優點是,它可以使得基于串行通信的設備無需物理串行端口即可與現代計算機系統通信,因為很多新式的計算機已經去除了傳統的RS-232串行接口。此外,許多操作系統對CDC有內置的支持,這意味著不需要額外安裝驅動程序就可以識別和使用這些設備,簡化了用戶的操作體驗。

二、虛擬串口

1.cubemx配置

我的實驗板子是野火的f429挑戰者v1,他比較奇怪的一點是他用的應該是全速的usb,但是引腳用的是高速usb的引腳,然后有id信號線,看起來像是高速的,但是我又沒看到高速的usb phy。是我的理解有問題嗎,有沒有知道的能解答一下。
在這里插入圖片描述

1.我們選用高速usb,然后選擇內部低速的phy,這樣使用的usb,最高速度為12Mbit每秒。

其他的選項不做處理。
在這里插入圖片描述

2.USB_DEVICE cdc類配置

需要注意的是,要確定自己的是大存儲芯片還是小存儲芯片,如果是f103c8t6,這里的TX 和 RX buffer size 應該是1024

在這里插入圖片描述

3.時鐘配置,USB需要使能外設時鐘,且好像只能倍頻為48M

我一開始倍頻為45M時,會顯示時鐘錯誤。而且時鐘最好在使能usb之后再去配置,我遇到過先配置了時鐘,再配置usb,然后后面時鐘再配置的時候,usb外設時鐘一直沒有使能。
在這里插入圖片描述

4.最小的堆棧大小也設置的大一點,不然端口識別會出現錯誤。

在這里插入圖片描述

5.發送和接收函數

接收函數

static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len)
{/* USER CODE BEGIN 11 */USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]);USBD_CDC_ReceivePacket(&hUsbDeviceHS);// CDC_Transmit_HS(Buf,*Len);user_usb_vpc_func(Buf,*Len);//用戶定義函數return (USBD_OK);/* USER CODE END 11 */
}

發送函數

void user_usb_vpc_func(uint8_t* Buf, uint32_t Len)
{CDC_Transmit_HS(Buf,Len);//數據回環發送
}

2.使用usb的dfu實現iap

dfu:Device Firmware Upgrade
iap:In-Application Programming,IAP指的是在不使用外部編程器的情況下,在設備正在運行的應用程序中直接對存儲在其內部或外部閃存中的固件進行編程的能力
我們常用的iap手段有
串口:優點是方便,硬件涉及簡單,缺點是速度慢,速度在幾KB撐死了,而且在出品后的現場環境中,串口往往沒有引出來。
sd:優點是速度快,就算是普通的sd卡接口,也能到25M,使用簡單,不需要上位機。缺點,如果需要頻繁的升級則需要頻繁的插拔和sd卡中的固件更新。而且需要額外的器件(sd卡)。
usb:優點是速度較快,全速在12M,高速可以上百兆(單片機會限速),而且使用方便,不用頻繁的插拔。缺點:需要上位機。
網絡:優點是速度快,幾十兆肯定能做到,使用方便,不用插拔。缺點,硬件成本較,技術要求相對較高,而且現場環境未必能支持你做網絡升級。

我在h7上做開發的時候,因為程序非常大,燒錄很慢,所以我就是用高速usb燒錄,然后仿真不擦除的去做調試。

要使用iap,我們需要兩個工程,一個是正常的應用程序,一個引導程序。
引導程序(bootloader)是用來做接收新固件程序和擦除改寫應用程序的flash的。
關于iap的介紹可以看這個
STM32 之八 在線升級(IAP)超詳細圖解 及 需要注意的問題解決

簡單來舉例說明,我需要兩個工程APP(用戶應用程序)和bootloader,stm32f429的flash程序起始地址為0x08000000,大小為0x100000。
其中一半用來做bootloader的程序地址,一半用來做app的程序地址。(具體分配可以隨意來,app的大小可以稍微大一點。但是注意flash的地址是按頁劃分的,所以兩個程序分配的大小也需要按頁來分。)
bootloader addr:0x08000000,size:0x100000(因為他需要訪問app的flash地址并作擦除和寫入操作)

在這里插入圖片描述
app addr:0x08080000,size:0x80000
在這里插入圖片描述

1.cubemx

usb配置和虛擬串口一樣。

在這里插入圖片描述

這是軟件實現流程
在這里插入圖片描述

3.代碼

usbd_dfu_if.c


/* USER CODE BEGIN PRIVATE_DEFINES */
#define FLASH_ERASE_TIME (uint16_t)50
#define FLASH_PROGRAM_TIME (uint16_t)50
// APP存放的結束地址
#define USBD_DFU_APP_END_ADD 0x08100000
// FLASH頁大小
#define FLASH_PAGE_SIZE 0x800U // 2K#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000)  /* Base address of Sector 0, 16 Kbytes   */
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000)  /* Base address of Sector 1, 16 Kbytes   */
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000)  /* Base address of Sector 2, 16 Kbytes   */
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000)  /* Base address of Sector 3, 16 Kbytes   */
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000)  /* Base address of Sector 4, 64 Kbytes   */
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000)  /* Base address of Sector 5, 128 Kbytes  */
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000)  /* Base address of Sector 6, 128 Kbytes  */
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000)  /* Base address of Sector 7, 128 Kbytes  */
#define ADDR_FLASH_SECTOR_8 ((uint32_t)0x08080000)  /* Base address of Sector 8, 128 Kbytes  */
#define ADDR_FLASH_SECTOR_9 ((uint32_t)0x080A0000)  /* Base address of Sector 9, 128 Kbytes  */
#define ADDR_FLASH_SECTOR_10 ((uint32_t)0x080C0000) /* Base address of Sector 10, 128 Kbytes  */
#define ADDR_FLASH_SECTOR_11 ((uint32_t)0x080E0000) /* Base address of Sector 11, 128 Kbytes  */
/* USER CODE END PRIVATE_DEFINES */
/*** @}*//** @defgroup USBD_DFU_Private_Macros* @brief Private macros.* @{*//* USER CODE BEGIN PRIVATE_MACRO *//* USER CODE END PRIVATE_MACRO *//*** @}*//** @defgroup USBD_DFU_Private_Variables* @brief Private variables.* @{*//* USER CODE BEGIN PRIVATE_VARIABLES *//* USER CODE END PRIVATE_VARIABLES *//*** @}*//** @defgroup USBD_DFU_Exported_Variables* @brief Public variables.* @{*/extern USBD_HandleTypeDef hUsbDeviceHS;/* USER CODE BEGIN EXPORTED_VARIABLES *//* USER CODE END EXPORTED_VARIABLES *//*** @}*//** @defgroup USBD_DFU_Private_FunctionPrototypes* @brief Private functions declaration.* @{*/static uint16_t MEM_If_Init_HS(void);
static uint16_t MEM_If_Erase_HS(uint32_t Add);
static uint16_t MEM_If_Write_HS(uint8_t *src, uint8_t *dest, uint32_t Len);
static uint8_t *MEM_If_Read_HS(uint8_t *src, uint8_t *dest, uint32_t Len);
static uint16_t MEM_If_DeInit_HS(void);
static uint16_t MEM_If_GetStatus_HS(uint32_t Add, uint8_t Cmd, uint8_t *buffer);/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */
static uint32_t GetSector(uint32_t Address);/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION *//*** @}*/#if defined(__ICCARM__) /* IAR Compiler */
#pragma data_alignment = 4
#endif__ALIGN_BEGIN USBD_DFU_MediaTypeDef USBD_DFU_fops_HS __ALIGN_END ={(uint8_t *)FLASH_DESC_STR,MEM_If_Init_HS,MEM_If_DeInit_HS,MEM_If_Erase_HS,MEM_If_Write_HS,MEM_If_Read_HS,MEM_If_GetStatus_HS};/* Private functions ---------------------------------------------------------*//*** @brief  Memory initialization routine.* @retval USBD_OK if operation is successful, MAL_FAIL else.*/
uint16_t MEM_If_Init_HS(void)
{/* USER CODE BEGIN 6 */// unlock inter flashHAL_FLASH_Unlock();// clear the flash flag bit__HAL_FLASH_CLEAR_FLAG(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR | FLASH_FLAG_PGSERR);return (USBD_OK);/* USER CODE END 6 */
}/*** @brief  De-Initializes Memory.* @retval USBD_OK if operation is successful, MAL_FAIL else.*/
uint16_t MEM_If_DeInit_HS(void)
{/* USER CODE BEGIN 7 */HAL_FLASH_Lock();return (USBD_OK);/* USER CODE END 7 */
}/*** @brief  Erase sector.* @param  Add: Address of sector to be erased.* @retval USBD_OK if operation is successful, MAL_FAIL else.*/
uint16_t MEM_If_Erase_HS(uint32_t Add)
{/* USER CODE BEGIN 8 *//*擦除整個APP程序存放的空間,即是0x08080000-0x08010000*//*因為起始地址是0x8000000,而Size是0x100000,所以MCU存放代碼的最后一個區域的地址為0x8100000。而DFU占了其中的0x80000的空間。*/uint32_t UserStartSector;uint32_t SectorError;FLASH_EraseInitTypeDef pEraseInit;// Unlock the Flash to enable the flash control register accessMEM_If_Init_FS();UserStartSector = GetSector(Add);pEraseInit.TypeErase = TYPEERASE_SECTORS;pEraseInit.Sector = UserStartSector;pEraseInit.NbSectors = 4; // 需要擦除的扇區數,扇區8-11pEraseInit.VoltageRange = VOLTAGE_RANGE_3;if (HAL_FLASHEx_Erase(&pEraseInit, &SectorError) != HAL_OK){/* Error occurred while page erase */return (USBD_FAIL);}return (USBD_OK);/* USER CODE END 8 */
}/*** @brief  Memory write routine.* @param  src: Pointer to the source buffer. Address to be written to.* @param  dest: Pointer to the destination buffer.* @param  Len: Number of data to be written (in bytes).* @retval USBD_OK if operation is successful, MAL_FAIL else.*/
uint16_t MEM_If_Write_HS(uint8_t *src, uint8_t *dest, uint32_t Len)
{/* USER CODE BEGIN 9 */uint32_t i = 0;for (i = 0; i < Len; i += 4){if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_WORD, (uint32_t)(dest + i), *(uint32_t *)(src + i)) == HAL_OK){if (*(uint32_t *)(src + i) != *(uint32_t *)(dest + i)){return USBD_FAIL;}}else{return USBD_FAIL;}}return (USBD_OK);/* USER CODE END 9 */
}/*** @brief  Memory read routine.* @param  src: Pointer to the source buffer. Address to be written to.* @param  dest: Pointer to the destination buffer.* @param  Len: Number of data to be read (in bytes).* @retval Pointer to the physical address where data should be read.*/
uint8_t *MEM_If_Read_HS(uint8_t *src, uint8_t *dest, uint32_t Len)
{/* Return a valid address to avoid HardFault *//* USER CODE BEGIN 10 */uint32_t i = 0;uint8_t *psrc = src;for (i = 0; i < Len; i++){dest[i] = *psrc++;}return (uint8_t *)(dest);/* USER CODE END 10 */
}/*** @brief  Get status routine.* @param  Add: Address to be read from.* @param  Cmd: Number of data to be read (in bytes).* @param  buffer: used for returning the time necessary for a program or an erase operation* @retval 0 if operation is successful*/
uint16_t MEM_If_GetStatus_HS(uint32_t Add, uint8_t Cmd, uint8_t *buffer)
{/* USER CODE BEGIN 11 */switch (Cmd){case DFU_MEDIA_PROGRAM:buffer[1] = (uint8_t)FLASH_PROGRAM_TIME;buffer[2] = (uint8_t)(FLASH_PROGRAM_TIME << 8);buffer[3] = 0;break;case DFU_MEDIA_ERASE:buffer[1] = (uint8_t)FLASH_ERASE_TIME;buffer[2] = (uint8_t)(FLASH_ERASE_TIME << 8);buffer[3] = 0;default:break;}return (USBD_OK);/* USER CODE END 11 */
}/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION *//*** @brief  根據輸入的地址給出它所在的sector*					例如:uwStartSector = GetSector(FLASH_USER_START_ADDR);uwEndSector = GetSector(FLASH_USER_END_ADDR);* @param  Address:地址* @retval 地址所在的sector*/
static uint32_t GetSector(uint32_t Address)
{uint32_t sector = 0;if ((Address < ADDR_FLASH_SECTOR_1) && (Address >= ADDR_FLASH_SECTOR_0)){sector = FLASH_SECTOR_0;}else if ((Address < ADDR_FLASH_SECTOR_2) && (Address >= ADDR_FLASH_SECTOR_1)){sector = FLASH_SECTOR_1;}else if ((Address < ADDR_FLASH_SECTOR_3) && (Address >= ADDR_FLASH_SECTOR_2)){sector = FLASH_SECTOR_2;}else if ((Address < ADDR_FLASH_SECTOR_4) && (Address >= ADDR_FLASH_SECTOR_3)){sector = FLASH_SECTOR_3;}else if ((Address < ADDR_FLASH_SECTOR_5) && (Address >= ADDR_FLASH_SECTOR_4)){sector = FLASH_SECTOR_4;}else if ((Address < ADDR_FLASH_SECTOR_6) && (Address >= ADDR_FLASH_SECTOR_5)){sector = FLASH_SECTOR_5;}else if ((Address < ADDR_FLASH_SECTOR_7) && (Address >= ADDR_FLASH_SECTOR_6)){sector = FLASH_SECTOR_6;}else if ((Address < ADDR_FLASH_SECTOR_8) && (Address >= ADDR_FLASH_SECTOR_7)){sector = FLASH_SECTOR_7;}else if ((Address < ADDR_FLASH_SECTOR_9) && (Address >= ADDR_FLASH_SECTOR_8)){sector = FLASH_SECTOR_8;}else if ((Address < ADDR_FLASH_SECTOR_10) && (Address >= ADDR_FLASH_SECTOR_9)){sector = FLASH_SECTOR_9;}else if ((Address < ADDR_FLASH_SECTOR_11) && (Address >= ADDR_FLASH_SECTOR_10)){sector = FLASH_SECTOR_10;}else // Address > ADDR_FLASH_SECTOR_11{sector = FLASH_SECTOR_11;}return sector;
}/************************************************************************* * *@brief 跳轉到應用程序* * *
************************************************************************/
void JumpToApp(void)
{typedef void (*pFunction)(void);static pFunction JumpToApplication;static uint32_t JumpAddress;/* Test if user code is programmed starting from USBD_DFU_APP_DEFAULT_ADD * address */if (((*(__IO uint32_t *)USBD_DFU_APP_DEFAULT_ADD) & 0x2FFE0000) == 0x20000000){//bootmod key not push ,go to app/* Jump to user application */JumpAddress = *(__IO uint32_t *)(USBD_DFU_APP_DEFAULT_ADD + 4);JumpToApplication = (pFunction)JumpAddress;/* Initialize user application's Stack Pointer */__set_MSP((*(__IO uint32_t *)USBD_DFU_APP_DEFAULT_ADD));__disable_irq(); //shut down interruptJumpToApplication();}
}

main.c

int main(void)
{/* USER CODE BEGIN 1 *//* USER CODE END 1 *//* MCU Configuration--------------------------------------------------------*//* Reset of all peripherals, Initializes the Flash interface and the Systick. */HAL_Init();/* USER CODE BEGIN Init *//* USER CODE END Init *//* Configure the system clock */SystemClock_Config();/* USER CODE BEGIN SysInit *//* USER CODE END SysInit *//* Initialize all configured peripherals */MX_GPIO_Init();// MX_USB_DEVICE_Init();/* USER CODE BEGIN 2 */if (HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) == GPIO_PIN_RESET){JumpToApp();}printf("press key2 to download");/* USER CODE END 2 *//* Infinite loop *//* USER CODE BEGIN WHILE */while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */if(HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin) == GPIO_PIN_SET){MX_USB_DEVICE_Init();}}/* USER CODE END 3 */
}

tips

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/895554.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/895554.shtml
英文地址,請注明出處:http://en.pswp.cn/news/895554.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HCIA項目實踐--靜態路由的綜合實驗

八 靜態路由綜合實驗 &#xff08;1&#xff09;劃分網段 # 192.168.1.0 24#分析&#xff1a;每個路由器存在兩個環回接口&#xff0c;可以把兩個環回接口分配一個環回地址&#xff0c;所以是四個環回&#xff0c;一個骨干&#xff0c;這樣分配&#xff0c;不會出現路由黑洞#19…

C++基礎知識(三)之結構體、共同體、枚舉、引用、函數重載

九、結構體、共同體和枚舉 1、結構體的基本概念 結構體是用戶自定義的類型&#xff0c;可以將多種數據的表示合并到一起&#xff0c;描述一個完整的對象。 使用結構體有兩個步驟&#xff1a;1&#xff09;定義結構體描述&#xff08;類型&#xff09;&#xff1b;2&#xff…

通過內網穿透ssh實現遠程對家里的linux進行終端操作和編程

內網穿透就是將自己的地址當穿透到公網ip&#xff0c;這一就可以在外網訪問了(因為大部分人ip都是動態分配的)&#xff0c;以適用ssh遠程連接&#xff0c;我這里用的是極點云cpolar&#xff0c;反正也是黑框編程&#xff0c;免費就行了。我是ubuntu虛擬機&#xff0c;在虛擬機上…

Redis 數據類型 Zset 有序集合

有序集合相對于字符串、列表、哈希、集合來說會有?些陌?。它保留了集合不能有重復成員的特點&#xff0c;但與集合不同的是&#xff0c;有序集合中的每個元素都有?個唯?的浮點類型的分數&#xff08;score&#xff09;與之關 聯&#xff0c;著使得有序集合中的元素是可以維…

PHP 中的除以零錯誤

除以零錯誤&#xff08;Division by zero&#xff09;是指數字除以零的情況&#xff0c; 這在數學上是未定義的。在 PHP 中&#xff0c;處理這種錯誤的方式取決于 PHP 版本&#xff1a; PHP 7&#xff1a; 使用 / 運算符會產生一個警告 (E_WARNING) 并返回 false。 使用 intd…

基于springboot軌道交通安全評估系統(源碼+lw+部署文檔+講解),源碼可白嫖!

摘要 時代在飛速進步&#xff0c;每個行業都在努力發展現在先進技術&#xff0c;通過這些先進的技術來提高自己的水平和優勢&#xff0c;軌道交通安全評估管理當然不能排除在外。軌道交通安全評估系統是在實際應用和軟件工程的開發原理之上&#xff0c;運用Java語言以及Spring…

前端面試題目---頁面抖動的原因、如何避免、如何解決

前端頁面抖動是一個常見且影響用戶體驗的問題&#xff0c;下面將從抖動發生的場景、解決辦法以及預防措施三個方面進行詳細闡述。 頁面抖動發生的場景 1. 元素尺寸動態變化 圖片加載&#xff1a;當頁面中圖片的寬高沒有預先設定&#xff0c;在圖片加載完成后&#xff0c;其實…

vue知識點3

1.使用componenent的好處 符合高內聚&#xff0c;低耦合。一個組件出問題了&#xff0c;不會影響其他組件。 2.vue版本決定了一些使用框架的版本 vue2使用的路由版本只能到3 3.路由的配置介紹一下 a.安裝路由的插件 npm install vue-router3 b.整理文件路徑 將路徑整理…

如何測試和驗證CVE-2024-1430:Netgear R7000 路由器信息泄露漏洞分析

CVE-2024-1430 是一個影響 Netgear R7000 路由器的安全漏洞&#xff0c;漏洞來源于該路由器 Web 管理界面的信息泄露問題。攻擊者通過訪問 /currentsetting.htm 文件&#xff0c;可能泄露敏感信息&#xff0c;如 Wi-Fi 密碼等。 在測試和驗證 CVE-2024-1430 時&#xff0c;您需…

數據結構:哈夫曼樹

1.概念 哈夫曼樹&#xff08;Huffman Tree&#xff09;是一種用于數據壓縮的二叉樹&#xff0c;由大衛哈夫曼&#xff08;David A. Huffman&#xff09;于1952年提出。它通過構建最優二叉樹來實現數據的高效壓縮&#xff0c;廣泛應用于文件壓縮、圖像壓縮等領域。 哈夫曼樹的…

UE5.2后 Bake Out Materials失效

這個問題出現在5.3&#xff0c;5.4&#xff0c;5.5沒有測試 烘焙貼圖后會找不到貼圖位置&#xff0c; 這個是5.2的正常狀態 默認是生成在模型當前目錄里&#xff0c;包括新的材質 但是這個bug會讓材質和貼圖都消失&#xff0c;無法定位 暫時沒有辦法解決&#xff0c;等官方 …

ADC 的音頻實驗,無線收發模塊( nRF24L01)

nRF24L01 采用 QFN20 封裝&#xff0c;有 20 個引腳&#xff0c;以下是各引腳的詳細介紹&#xff1a; 1. 電源引腳 ? VDD&#xff1a;電源輸入端&#xff0c;一般接 3V 電源&#xff0c;為芯片提供工作電壓&#xff0c;供電電壓范圍為 1.9V&#xff5e;3.6V。 ? VSS&#xf…

基于HTML5 Canvas 和 JavaScript 實現的煙花動畫效果

以下是一個使用 HTML5 Canvas 和 JavaScript 實現的煙花動畫效果代碼盒子: <!DOCTYPE html> <html> <head><title>煙花效果

C++課程設計 運動會分數統計(含源碼)

C++課程設計 運動會分數統計 一、題目描述(一)問題描述(二)基本要求二、程序設計文檔1. 項目概述1.1 項目背景1.2 功能需求1.3 非功能需求2. 系統設計2.1 數據結構設計2.1.1 `School` 結構體2.1.2 `Project` 結構體2.2 功能模塊設計2.2.1 主菜單2.2.2 輸入/修改項目成績2.2…

【音視頻】RTSP拉流: RTP負載AAC詳解(三)

此文為系列文章&#xff0c;此系列主要講解RTSP客戶端的拉流及播放&#xff0c;文章持續更新&#xff0c;會從rtsp的基本協議講起&#xff0c;如何一步步實現音視頻的拉流過程&#xff0c;包括一系列涉及到的協議&#xff0c;rtsp&#xff0c;sdp&#xff0c; rtp&#xff08;本…

Dockerfiles 的 Top 10 常見 DevOps/SRE 面試問題及答案

1. RUN 和 CMD 之間有什么區別&#xff1f; RUN : 在鏡像構建過程中執行命令&#xff0c;創建一個新的層。通常用于安裝軟件包。 示例: RUN apt-get update && apt-get install -y curlCMD : 指定容器啟動時默認運行的命令。它在運行時執行&#xff0c;而不是在構建過程…

【ARM】JTAG接口介紹

1、 文檔目標 對 JTAG 接口有更多的認識&#xff0c;在遇到關于 JTAG 接口問題時有一些排查的思路。 2、 問題場景 在使用調試器過程時&#xff0c;免不了要接觸到 JTAG 接口&#xff0c;當出現連接不上時&#xff0c;就不知道從哪來進行排查。 3、軟硬件環境 1 軟件版本&am…

opencascade 獲取edge起始點 會出現終點與實際不同的情況

在使用 OpenCASCADE 獲取 TopoDS_Edge 的起始點和終點時&#xff0c;可能會出現終點與實際不一致的情況。這通常是由于以下原因導致的&#xff1a; 幾何曲線的方向問題&#xff1a;在某些情況下&#xff0c;幾何曲線的方向可能與拓撲邊的方向不一致&#xff0c;導致通過幾何曲線…

【電腦】u盤重裝win7

u盤必須8GB以上 1. CPU型號 首先查看CPU的型號看看到底能不能裝win7 2. 下載光盤映像文件 網址 看電腦是多少位的機器(32位下載x86 64位下載x64) 一共是這么多個版本按需下載對應的版本 電腦小白推薦無腦下載旗艦版 將鏈接復制到迅雷進行下載 3. 下載軟碟通 網址 下…

C++-AVL樹

一、AVL樹的概念 1.二叉搜索樹 二叉搜索樹&#xff08;BST&#xff0c;Binary Search Tree&#xff09;&#xff0c;也稱二叉排序樹或二叉查找樹。 二叉搜索樹&#xff1a;一棵二叉樹&#xff0c;可以為空&#xff1b;如果不為空&#xff0c;滿足以下性質&#xff1a; 非空左子…