USB學習【13】STM32+USB接收數據過程詳解

目錄

  • 1.官方的描述
  • 2.HAL的流程
    • 把接收到的數據從PMA拷貝到用戶自己定義的空間中
  • 3.處理接收到的數據
  • 4.最后再次開啟準備接收工作

1.官方的描述

在這里插入圖片描述
在這里插入圖片描述
在這里插入圖片描述

2.HAL的流程

以上的官方說法我們暫時按下不表。
如果接收到數據,會激活中斷進入到USB_LP_CAN1_RX0_IRQHandler()->HAL_PCD_IRQHandler()
因為所有的中斷事件共用一個中斷函數,所以第一步要進行中斷類型檢測,中斷的信息都寫在了ISTR寄存器中,判斷代碼如下:

  if (__HAL_PCD_GET_FLAG(hpcd, USB_ISTR_CTR)){/* servicing of the endpoint correct transfer interrupt *//* clear of the CTR flag into the sub */(void)PCD_EP_ISR_Handler(hpcd);}

下一步自然而然的就是執行這個函數:

 (void)PCD_EP_ISR_Handler(hpcd);

既然進入中斷了,我們還是要驗證一下ISTR的正確傳輸中斷有沒有置位

 while ((hpcd->Instance->ISTR & USB_ISTR_CTR) != 0U)

在這里插入圖片描述
技術手冊提示:可以通過DIR和EPID來判斷是哪個端點,接下來的代碼也是符合這個步驟的,下面的步驟是判斷了哪個端點,暫時沒有判斷方向,一些邏輯執行完畢后,就會判斷方向了,詳細看后面的代碼。

wIstr = hpcd->Instance->ISTR;/* extract highest priority endpoint number */
epindex = (uint8_t)(wIstr & USB_ISTR_EP_ID);if (epindex == 0U)
{
}else if (epindex == 1U){//我們真正使用的端點}

下一步拿到端點寄存器的數據,看看CTR_RX有沒有接收到數據
如果接收到第一步就是把標志位清除掉

下面的圖片,發現正確接收標志位置1,CTRM置位,就會產生中斷,CTRM位說,如果中斷狀態寄存器置1就會產生中斷,這里面就說明任何端點正確接收置1后,中斷狀態寄存器也置1,這是一個聯動反應

  wEPVal = PCD_GET_ENDPOINT(hpcd->Instance, epindex);if ((wEPVal & USB_EP_CTR_RX) != 0U){/* clear int flag */PCD_CLEAR_RX_EP_CTR(hpcd->Instance, epindex);

在這里插入圖片描述
緊接著,HAL庫把OUT_ep結構體拿過來,這里面記錄了所有規定的端點信息,在初始化階段把這部分信息預先儲存到結構體里面的。

ep = &hpcd->OUT_ep[epindex];/* OUT Single Buffering */if (ep->doublebuffer == 0U){count = (uint16_t)PCD_GET_EP_RX_CNT(hpcd->Instance, ep->num);if (count != 0U){USB_ReadPMA(hpcd->Instance, ep->xfer_buff, ep->pmaadress, count);}}

其中 count = (uint16_t)PCD_GET_EP_RX_CNT(hpcd->Instance, ep->num);這條語句,

//先拿到對應端點緩沖表USB_COUNTn_RX的數據
#define PCD_EP_RX_CNT(USBx, bEpNum) ((uint16_t *)((((uint32_t)(USBx)->BTABLE\+ ((uint32_t)(bEpNum) * 8U) + 6U) * PMA_ACCESS) + ((uint32_t)(USBx) + 0x400U)))
//把USB_COUNTn_RX中的高于9位的信息刪除掉,只保留COUNTn_RX(實際接收到的數據)
#define PCD_GET_EP_RX_CNT(USBx, bEpNum)        ((uint32_t)(*PCD_EP_RX_CNT((USBx), (bEpNum))) & 0x3ffU)

在這里插入圖片描述
到了對關鍵的一步,就數據從緩沖區里面,拷貝到用戶的自定義區

if (count != 0U){USB_ReadPMA(hpcd->Instance, ep->xfer_buff, ep->pmaadress, count);}

注意一下參數:從上面看,最后接收到的數據,其實是放在對應端點結構體的xfter_buff里面的,現在就是只要拿到對應端點結構體的句柄,就能找到接收到的數據
(這個在接收數據完成之后定義的連接,需要結合后面代碼看)
在這里插入圖片描述

把接收到的數據從PMA拷貝到用戶自己定義的空間中

/*** @brief Copy data from packet memory area (PMA) to user memory buffer* @param   USBx USB peripheral instance register address.* @param   pbUsrBuf pointer to user memory area.* @param   wPMABufAddr address into PMA.* @param   wNBytes no. of bytes to be copied.* @retval None*/
/*** @brief Copy data from packet memory area (PMA) to user memory buffer* @param   USBx USB peripheral instance register address.* @param   pbUsrBuf pointer to user memory area.* @param   wPMABufAddr address into PMA.* @param   wNBytes no. of bytes to be copied.* @retval None*/
void USB_ReadPMA(USB_TypeDef *USBx, uint8_t *pbUsrBuf, uint16_t wPMABufAddr, uint16_t wNBytes)
{// 計算需要處理的16位數據單元的數量(每個單元包含2個字節),比如接收了64字節,>>1,變成了32字節uint32_t n = (uint32_t)wNBytes >> 1;// 計算USB外設的基地址uint32_t BaseAddr = (uint32_t)USBx;// 用于循環計數和臨時存儲數據的變量uint32_t i, temp;// 定義一個指向PMA中16位數據的指針__IO uint16_t *pdwVal;// 定義一個指向用戶緩沖區的指針uint8_t *pBuf = pbUsrBuf;// 計算PMA中目標數據的起始地址pdwVal = (__IO uint16_t *)(BaseAddr + 0x400U + ((uint32_t)wPMABufAddr * PMA_ACCESS));// 循環處理每個16位數據單元for (i = n; i != 0U; i--){// 從PMA中讀取一個16位數據temp = *(__IO uint16_t *)pdwVal;pdwVal++;// 將16位數據的低8位存儲到用戶緩沖區*pBuf = (uint8_t)((temp >> 0) & 0xFFU);pBuf++;// 將16位數據的高8位存儲到用戶緩沖區*pBuf = (uint8_t)((temp >> 8) & 0xFFU);pBuf++;// 如果PMA訪問的步長大于1,則跳過一個額外的單元(可能是因為硬件設計)
#if PMA_ACCESS > 1UpdwVal++;
#endif}// 如果剩余的字節數不是2的倍數(即還有1個字節未處理)if ((wNBytes % 2U) != 0U){// 從PMA中讀取最后一個字節temp = *pdwVal;*pBuf = (uint8_t)((temp >> 0) & 0xFFU);}
}
#endif /* defined (USB) */

關于接收完成標志
可以看到這里有一個判斷條件if ((ep->xfer_len == 0U) || (count <= ep->maxpacket)),其中ep->xfer_len == 0U)是給控制傳輸用的,他需要嚴格的控制包數,count <= ep->maxpacket是給中斷傳輸用的,只要不大于最大包,就是傳輸完成(短包)

   /* multi-packet on the NON control OUT endpoint */ep->xfer_count += count;ep->xfer_buff += count;if ((ep->xfer_len == 0U) || (count <= ep->maxpacket)){/* RX COMPLETE */

3.處理接收到的數據

在這里插入圖片描述

在這里插入圖片描述

	USBD_StatusTypeDef USBD_LL_DataOutStage(USBD_HandleTypeDef *pdev,uint8_t epnum, uint8_t *pdata)
{USBD_EndpointTypeDef *pep;if (epnum == 0U){//一些實現代碼}else if ((pdev->pClass->DataOut != NULL) &&(pdev->dev_state == USBD_STATE_CONFIGURED)){pdev->pClass->DataOut(pdev, epnum);}else{/* should never be in this condition */return USBD_FAIL;}return USBD_OK;
}
USBD_ClassTypeDef  USBD_CUSTOM_HID =
{USBD_CUSTOM_HID_Init,USBD_CUSTOM_HID_DeInit,USBD_CUSTOM_HID_Setup,NULL, /*EP0_TxSent*/USBD_CUSTOM_HID_EP0_RxReady, /*EP0_RxReady*/ /* STATUS STAGE IN */USBD_CUSTOM_HID_DataIn, /*DataIn*/USBD_CUSTOM_HID_DataOut,NULL, /*SOF */NULL,NULL,USBD_CUSTOM_HID_GetHSCfgDesc,USBD_CUSTOM_HID_GetFSCfgDesc,USBD_CUSTOM_HID_GetOtherSpeedCfgDesc,USBD_CUSTOM_HID_GetDeviceQualifierDesc,
};

閱讀上面代碼,發現最終執行了pdev->pClass->DataOut(pdev, epnum);這個函數指針。
下圖是在初始化階段就綁定好的函數指針,可以找到真正執行的函數名字是
USBD_CUSTOM_HID_DataOut->

/**
/*** @brief  USBD_CUSTOM_HID_DataOut*         handle data OUT Stage* @param  pdev: device instance* @param  epnum: endpoint index* @retval status*/
static uint8_t  USBD_CUSTOM_HID_DataOut(USBD_HandleTypeDef *pdev,uint8_t epnum)
{USBD_CUSTOM_HID_HandleTypeDef     *hhid = (USBD_CUSTOM_HID_HandleTypeDef *)pdev->pClassData;((USBD_CUSTOM_HID_ItfTypeDef *)pdev->pUserData)->OutEvent(epnum,hhid->Report_buf[0],hhid->Report_buf[1]);USBD_LL_PrepareReceive(pdev, CUSTOM_HID_EPOUT_ADDR, hhid->Report_buf,USBD_CUSTOMHID_OUTREPORT_BUF_SIZE);return USBD_OK;
}

下一步執行這個函數指針,實際是CUSTOM_HID_OutEvent_FS(uint8_t epnum, uint8_t event_idx, uint8_t state)這個函數

/*** @brief  Manage the CUSTOM HID class events* @param  event_idx: Event index* @param  state: Event state* @retval USBD_OK if all operations are OK else USBD_FAIL*/
static int8_t CUSTOM_HID_OutEvent_FS(uint8_t epnum, uint8_t event_idx, uint8_t state)
{/* USER CODE BEGIN 6 */USBD_CUSTOM_HID_HandleTypeDef  *hhid = (USBD_CUSTOM_HID_HandleTypeDef*)hUsbDeviceFS.pClassData;for( uint8_t i  = 0;i <USBD_CUSTOMHID_OUTREPORT_BUF_SIZE;i++){USB_Recive_Buffer[i] = hhid->Report_buf[i];}HID_RxCpltCallback((uint8_t)epnum,&USB_Recive_Buffer);return (USBD_OK);/* USER CODE END 6 */
}

這個函數又把把數據從(USBD_CUSTOM_HID_HandleTypeDef*)hUsbDeviceFS.pClassData.Report_buf,轉移到了我們自定義的區域,這個Report區域和對應端點的區域是相等的,在下面的再次準備接收階段定義好了
在這里插入圖片描述

并且調用了這個函數來解析數據HID_RxCpltCallback((uint8_t)epnum,&USB_Recive_Buffer);

4.最后再次開啟準備接收工作

在這里插入圖片描述
執行了這個函數

  USBD_LL_PrepareReceive(pdev, CUSTOM_HID_EPOUT_ADDR, hhid->Report_buf,USBD_CUSTOMHID_OUTREPORT_BUF_SIZE);HAL_StatusTypeDef HAL_PCD_EP_Receive(PCD_HandleTypeDef *hpcd, uint8_t ep_addr, uint8_t *pBuf, uint32_t len)
{PCD_EPTypeDef *ep;ep = &hpcd->OUT_ep[ep_addr & EP_ADDR_MSK];/*setup and start the Xfer */ep->xfer_buff = pBuf;ep->xfer_len = len;ep->xfer_count = 0U;ep->is_in = 0U;ep->num = ep_addr & EP_ADDR_MSK;if ((ep_addr & EP_ADDR_MSK) == 0U){(void)USB_EP0StartXfer(hpcd->Instance, ep);}else{(void)USB_EPStartXfer(hpcd->Instance, ep);}return HAL_OK;
}

上面函數,把接收端口的一些存數據的地址,接收長度,計數等等信息復位

AL_StatusTypeDef USB_EPStartXfer(USB_TypeDef *USBx, USB_EPTypeDef *ep)
{uint32_t len;uint16_t pmabuffer;uint16_t wEPVal;/* IN endpoint */if (ep->doublebuffer == 0U){/* Multi packet transfer */if (ep->xfer_len > ep->maxpacket){len = ep->maxpacket;ep->xfer_len -= len;}else{len = ep->xfer_len;ep->xfer_len = 0U;}/* configure and validate Rx endpoint */PCD_SET_EP_RX_CNT(USBx, ep->num, len);

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

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

相關文章

上海內推 | 上海算法創新研究院-上海交大聯合招收空間智能/具身智能算法實習生

最近這一兩周不少公司已開啟春招和實習招聘。 不同以往的是&#xff0c;當前職場環境已不再是那個雙向奔赴時代了。求職者在變多&#xff0c;HC 在變少&#xff0c;崗位要求還更高了。 最近&#xff0c;我們又陸續整理了很多大廠的面試題&#xff0c;幫助一些球友解惑答疑&am…

C語言速成12之指針:程序如何在內存迷宮里找寶藏?

程序員Feri一名12年的程序員,做過開發帶過團隊創過業,擅長Java、鴻蒙、嵌入式、人工智能等開發,專注于程序員成長的那點兒事,希望在成長的路上有你相伴&#xff01;君志所向,一往無前&#xff01; 0. 前言&#xff1a;程序如何在內存迷宮里找寶藏&#xff1f; 想象內存是一個巨…

部署n8n

https://github.com/n8n-io/n8n docker volume create n8n_data docker run -it --rm --name n8n -p 5678:5678 -v n8n_data:/home/node/.n8n docker.n8n.io/n8nio/n8n Discover 2192 Automation Workflows from the n8ns Community

ABP VNext + Orleans:Actor 模型下的分布式狀態管理最佳實踐

ABP VNext Orleans&#xff1a;Actor 模型下的分布式狀態管理最佳實踐 &#x1f680; &#x1f4da; 目錄 ABP VNext Orleans&#xff1a;Actor 模型下的分布式狀態管理最佳實踐 &#x1f680;一、引言&#xff1a;分布式系統的狀態挑戰 &#x1f4a1;二、架構圖與技術棧 &am…

構建安全AI風險識別大模型:CoT、訓練集與Agent vs. Fine-Tuning對比

構建安全AI風險識別大模型:CoT、訓練集與Agent vs. Fine-Tuning對比 安全AI風險識別大模型旨在通過自然語言處理(NLP)技術,檢測和分析潛在的安全威脅,如數據泄露、合規違規或惡意行為。本文從Chain-of-Thought (CoT)設計、訓練集構建、以及Agent-based方法與**AI直接調優…

Baklib內容中臺的主要構成是什么?

Baklib內容中臺核心架構 Baklib作為一站式知識管理平臺的核心載體&#xff0c;其架構設計圍繞智能搜索引擎優化技術與多終端適配響應系統展開。通過模塊化內容組件的靈活配置&#xff0c;企業可快速搭建知識庫、FAQ頁面及幫助中心等標準化場景&#xff0c;同時借助可視化數據看…

Ubuntu Desktop 24.04 常用軟件安裝步驟

文章目錄 Ubuntu Desktop 24.04 常用軟件安裝步驟Snipaste F1快捷截圖&#xff08;超方便 | 我6臺電腦每臺都用&#xff09;搜狗輸入法快速瀏覽工具 | 空格鍵快速預覽文件壁紙工具 | varietySSH 工具 | Termius 終端分屏工具 | TmuxCaffeine | 避免息屏小工具 一些設置將啟動臺…

詳細使用@rollup/plugin-inject的方式

rollup/plugin-inject 是一個 Rollup 插件&#xff0c;它允許你在構建時自動注入模塊中的變量引用&#xff0c;避免手動在每個文件中 import。Vite 使用的是 Rollup 構建底層&#xff0c;因此該插件在 Vite 項目中也適用。 一、使用場景 比如你希望在代碼中不手動寫 import { …

Day 0017:Web漏洞掃描(OpenVAS)解析

一、NVT腳本解析&#xff1a;漏洞檢測的“DNA” 1. NVT腳本結構 每個NVT腳本都是一個Lua腳本&#xff0c;包含以下核心模塊&#xff1a; lua -- 示例&#xff1a;檢測Apache HTTPd 2.4.49路徑穿越漏洞&#xff08;CVE-2021-41773&#xff09; script_id "1.3.6.1.4.1.…

【HarmonyOS Next之旅】DevEco Studio使用指南(二十六) -> 創建端云一體化開發工程

目錄 1 -> 創建HarmonyOS應用工程 1.1 -> 新建工程 1.1.1 -> 前提條件 1.1.2 -> 選擇模板 1.1.3 -> 配置工程信息 1.1.4 -> 關聯云開發資源 1.2 -> 工程初始化配置 1.2.1 -> 自動開通云開發服務 1.3 -> 端云一體化開發工程目錄結構 1.3.1…

Python 包管理工具 uv

Python 包管理工具 uv 是由 Astral 團隊&#xff08;知名工具 Ruff 的開發者&#xff09;基于 Rust 開發的新一代工具&#xff0c;旨在通過高性能和一體化設計革新 Python 生態的依賴管理體驗。以下是其核心特性、優勢及使用指南的全面解析&#xff1a; 一、uv 的核心優勢 極致…

何謂第二大腦?讀書筆記

2025/05/11 發表想法 每個人都是矛盾結合體&#xff0c;既想學到新知識、新的能力&#xff0c;又想沒辦法專注的學習&#xff0c;既無法專注有渴望學習新技能&#xff0c;逐漸會產生焦慮、失眠等負面癥狀&#xff0c;這就是現實社會現照&#xff0c;那怎么辦&#xff1f;我們能…

動態防御體系實戰:AI如何重構DDoS攻防邏輯

1. 傳統高防IP的靜態瓶頸 傳統高防IP依賴預定義規則庫&#xff0c;面對SYN Flood、CC攻擊等常見威脅時&#xff0c;常因規則更新滯后導致誤封合法流量。例如&#xff0c;某電商平臺遭遇HTTP慢速攻擊時&#xff0c;靜態閾值過濾無法區分正常用戶與攻擊者&#xff0c;導致訂單接…

為什么在設置 model.eval() 之后,pytorch模型的性能會很差?為什么 dropout 影響性能?| 深度學習

在深度學習的世界里&#xff0c;有一個看似簡單卻讓無數開發者困惑的現象&#xff1a; “為什么在訓練時模型表現良好&#xff0c;但設置 model.eval() 后&#xff0c;模型的性能卻顯著下降&#xff1f;” 這是一個讓人抓耳撓腮的問題&#xff0c;幾乎每一個使用 PyTorch 的研究…

[爬蟲知識] http協議

相關爬蟲專欄&#xff1a;JS逆向爬蟲實戰 爬蟲知識點合集 爬蟲實戰案例 引言&#xff1a;爬蟲與HTTP的不解之緣 爬蟲作用&#xff1a;模擬瀏覽器請求網頁為何要懂HTTP&#xff1a;http是網絡通信的基石&#xff0c;爬蟲抓取數據就是通過HTTP協議進行的&#xff0c;了解http有…

《Spark/Flink/Doris離線實時數倉開發》目錄

歡迎加入《Spark/Flink/Doris離線&實時數倉開發》付費專欄&#xff01;本專欄專為大數據工程師、數據分析師及準備大數據面試的求職者量身打造&#xff0c;聚焦Spark、Flink、Doris等核心技術&#xff0c;覆蓋離線與實時數倉開發的全流程。無論你是想快速上手項目、提升技術…

事務基礎概念

事務 事務是什么&#xff1f; 事務是一種機制&#xff0c;一個操作序列&#xff0c;包含了一組數據庫操作命令&#xff0c;并且把所有命令作為一個整體一起向系統提交或者撤銷操作請求&#xff0c;即統一這組命令要么一起執行&#xff0c;要么一起不執行 簡短概況就是&#…

四、【API 開發篇 (上)】:使用 Django REST Framework 構建項目與模塊 CRUD API

【API 開發篇 】&#xff1a;使用 Django REST Framework 構建項目與模塊 CRUD API 前言為什么選擇 Django REST Framework (DRF)&#xff1f;第一步&#xff1a;創建 Serializers (序列化器)第二步&#xff1a;創建 ViewSets (視圖集)第三步&#xff1a;配置 URLs (路由)第四步…

【北京盈達科技】GEO優化中的多模態了解

多模態數據處理領域&#xff0c;“模態”指的是不同類型的數據形式&#xff0c;每種模態都具有獨特的結構和信息表達方式。以下是12種可能的模態類型&#xff0c;這些模態在實際應用中可以根據具體場景進行組合和處理&#xff1a; 1. 文本模態 描述&#xff1a;以文字形式存在…

推進可解釋人工智能邁向類人智能討論總結分享

目錄 一、探索“可解釋人工智能”&#xff1a;AI如何從“黑箱”走向“透明大師” 二、走進可解釋人工智能&#xff1a;讓AI的決策變得透明 &#xff08;一&#xff09;幾種常見的特征導向方法 &#xff08;二&#xff09;像素級方法 1. 層次相關傳播&#xff08;LRP&#…