22.1 USB概述
USB是英文Universal Serial BUS(通用串行總線)的縮寫,是一個外部總線標準,用于規范電腦與外部設備的連接和通訊。是應用在PC領域的接口技術。USB接口支持設備的即插即用和熱插拔功能。是在1994年底由英特爾、康柏、IBM、Microsoft等多家公司聯合提出的。發展到現在已經有USB1.0/1.1/2.0/3.0/3.1等多個版本。目前用的最多的就是USB2.0和USB3.0,USB3.1目前已經開始普及。STM32F103自帶的USB符合USB2.0規范。標準USB共四根線組成,除VCC和GND外,另外為D+,D-,這兩根數據線采用的是差分電壓的方式進行數據傳輸的。在USB主機上,D-和D+都是接了15K的電阻到低的,所以在沒有設備接入的時候,D+、D-均是低電平。而在USB設備中,如果是高速設備,則會在D+上接一個1.5K的電阻到VCC,而如果是低速設備,則會在D-上接一個1.5K的電阻到VCC。這樣當設備接入主機的時候,主機就可以判斷是否有設備接入,并能判斷設備是高速設備還是低速設備。
STM32F1自帶一個USB從機控制器,符合USB規范通信,PC主機和微控制器之間的數據傳輸是通過共享一個專用的數據緩沖區來完成的,該數據緩沖區能被USB外設直接訪問,這塊專用數據緩沖區的大小由所使用的端點數目和每個端點最大的數據分組大小所決定,每個端點最大可使用512字節緩沖區,最多可用于16個單向或8個雙向端點。
USB模塊同PC主機通信,根據USB規范實現令牌分組的檢測,數據發送/接收的處理,和握手分組的處理。整個傳輸的格式由硬件完成,其中包括CRC的生成和校驗。每個端點都有一個緩沖區描述塊,描述該端點使用的緩沖區地址、大小和需要傳輸的字節數。當USB模塊識別出一個有效的功能/端點的令牌分組時,(如果需要傳輸數據并且端點已配置)隨之發生相關的數據傳輸。USB模塊通過一個內部的16位寄存器實現端口與專用緩沖區的數據交換。在所有的數據傳輸完成后,如果需要,則根據傳輸的方向,發送或接收適當的握手分組。在數據傳輸結束時,USB模塊將觸發與端點相關的中斷,通過讀狀態寄存器和/或者利用不同的中斷來處理。USB設備架構如圖所示。
USB的中斷映射單元:將可能產生中斷的USB事件映射到三個不同的NVIC請求線上:
(1)USB低優先級中斷(通道20):可由所有USB事件觸發(正確傳輸,USB復位等)。在處理中斷前應首先確定中斷源。
(2)USB高優先級中斷(通道19):僅由同步和雙緩沖批量傳輸的正確傳輸事件觸發,為保證最大的傳輸速率。
(3)USB喚醒中斷(通道42):由USB掛起模式的喚醒事件觸發。
注:USB和CAN共用一個專用的512字節的SRAM存儲器用于數據的發送和接收,因此不能同時使用USB和CAN(共享的SRAM被USB和CAN模塊互斥地訪問)。USB和CAN可以同時用于一個應用中但不能在同一個時間使用。
22.2 實驗例程
如果需要正常的使用STM32F1系列的USB模塊,就需要編寫USB驅動程序,這部分程序非常復雜,需要了解整個USB通信的詳細過程,針對這個問題,ST公司提供了一個官方的USB驅動庫,用戶可以通過直接移植官方驅動庫來實現USB讀寫控制。
我們現在直接利用官方的USB驅動源碼來通過計算機進行SD卡和Flash的讀寫,這里我們需要對官方源碼進行一些修改,用于實現這個效果。
22.2.1 USB源碼概述
USB Mass Storage類支持兩個傳輸協議:
(1)Bulk-Only傳輸(BOT)
(2)Control/Bulk/Interrupt傳輸(CBI)
MassStorage類規范定義了兩個類規定的請求:Get_Max_LUN和MassStorageReset,所有的MassStorage類設備都必須支持這兩個請求。
(1)Get_Max_LUN(bmRequestType=10100001 bandb Request=11111110b)用來確認設備支持的邏輯單元數。MaxLUN的值必須是0~15。注意:LUN是從0開始的。主機不能向不存在的LUN發送CBW,本章我們定義MaxLUN的值為1,即代表2個邏輯單元。
(2)MassStorageReset(bmRequestType=00100001 bandb Request=11111111b)用來復位MassStorage設備及其相關接口。
支持BOT傳輸的MassStorage設備接口描述符要求如下:
(1)接口類代碼bInterfaceClass=08h,表示為MassStorage設備。
(2)接口類子代碼bInterfaceSubClass=06h,表示設備支持SCSIPrimaryCommand-2(SPC-2)。
(3)協議代碼bInterfaceProtocol有3種:0x00、0x01、0x50,前兩種需要使用中斷傳輸,最后一種僅使用批量傳輸(BOT)。
(4)支持BOT的設備必須支持最少3個endpoint:Control,Bulk-In和Bulk-Out。USB2.0的規范定義了控制端點0。Bulk-In端點用來從設備向主機傳送數據(本章用端點1實現)。Bulk-Out端點用來從主機向設備傳送數據(本章用端點2實現)。
ST官方的例程是通過USB來讀寫SD卡(SDIO方式)和Nand Falsh,支持2個邏輯單元,我們在官方例程的基礎上,只需要修改SD驅動部分代碼,并將對Nand Falsh的操作修改為對SPI Falsh的操作。只要這兩步完成了,剩下的就比較簡單了,對底層磁盤的讀寫,都是在mass_mal.c文件實現的,所以我們只需要修改該函數的MAL_Init、MAL_Write、MAL_Read和MAL_GetStatus等4個函數,與我們的SD卡和SPI Falsh對應起來即可。
22.2.2 源碼移植過程
(1)需要添加的文件如下表所示。
文件名 | 目錄 | 功能 |
---|---|---|
usb_core.c | ....\\USB\\CORE | 用于處理USB2.0協議 |
usb_init.c | 用于USB控制器的初始化 | |
usb_int.c | 負責USB的中斷處理 | |
usb_mem.c | 負責處理PMA數據,即STM32內部用于USB/CAN的專用數據緩沖區 | |
usb_regs.c | 負責USB控制寄存器的底層操作 | |
usb_sil.c | 為USB端點提供特殊簡化的讀寫訪問函數 | |
usb_desc.c | ...\\USB\\CONFIG | 用于虛擬通信端口描述符的處理 |
usb_endp.c | 用于非控制傳輸,處理正確傳輸中斷回調函數 | |
usb_istr.c | 用于處理USB中斷 | |
usb_prop.c | 用于處理所有虛擬通信端口相關事件,包括初始化,復位等 | |
usb_pwr.c | 用于管理USB的電源狀態 | |
usb_scsi.c | 與SCSI命令相關的所有處理 | |
scsi_data.c | 定義了SCSI數據 | |
memory.c | 定義USB通信的存儲區讀寫函數 | |
mass_mal.c | 定義了USB通信的讀寫操作底層函數接口 | |
usb_bot.c | 定義了BOT傳輸協議 |
(2)usb_prop.c文件修改
原文件
修改后文件
(3)memory.h文件修改
(4)mass_mal.h文件修改
(5)memory.c文件修改
(6)usb_bot.c文件修改
(7)mass_mal.c文件重寫
#include "platform_config.h"
#include "mass_mal.h"
#include "sdio_sdcard.h"
#include "w25q128.h"
long long Mass_Memory_Size[ MAX_LUN+1 ] ;
u32 Mass_Block_Size[ MAX_LUN+1 ] ;
u32 Mass_Block_Count[ MAX_LUN+1 ] ;
uint16_t MAL_Init( uint8_t lun )
{
u16 Status=MAL_OK ;
switch( lun )
{
case 0: break;
case 1: break;
default:return MAL_FAIL ;
}
return Status ;
}
uint16_t MAL_Write(uint8_t lun, uint64_t Memory_Offset, uint32_t *Writebuff, uint16_t Transfer_Length)
{
u8 STA ;
switch( lun )
{
//磁盤0為 SPI FLASH盤
case 0:
STA = 0 ;
W25QXX_Write( ( u8* )Writebuff, Memory_Offset, Transfer_Length ) ;
break ;
//磁盤1為SD卡
case 1:
STA = SD_WriteDisk( ( u8* )Writebuff, Memory_Offset>>9, Transfer_Length>>9 ) ;
break ;
default:
return MAL_FAIL ;
}
if( STA!=0 )
return MAL_FAIL ;
return MAL_OK ;
}
uint16_t MAL_Read( uint8_t lun, uint64_t Memory_Offset, uint32_t *Readbuff, uint16_t Transfer_Length )
{
u8 STA ;
switch( lun )
{
//磁盤0為 SPI FLASH盤
case 0 :
STA = 0 ;
W25QXX_Read( ( u8* )Readbuff, Memory_Offset, Transfer_Length ) ;
break;
//磁盤1為SD卡
case 1 :
STA = SD_ReadDisk( ( u8* )Readbuff, Memory_Offset>>9, Transfer_Length>>9 ) ;
break;
default:return MAL_FAIL ;
}
if( STA!=0 )
return MAL_FAIL ;
return MAL_OK ;
}
uint16_t MAL_GetStatus( uint8_t lun )
{
switch( lun )
{
case 0:return MAL_OK;
case 1:return MAL_OK;
default:return MAL_FAIL;
}
}
(8)hw_config.h文件重寫
#ifndef HW_CONFIG_H
#define HW_CONFIG_H
#include "platform_config.h"
#include "usb_type.h"
typedef enum
{
DISABLE = 0,
ENABLE = 1
}FunctionalState;
#define BULK_MAX_PACKET_SIZE 0x00000040 //包大小,最大64字節
void Led_RW_ON( void ) ; //LED開啟
void Led_RW_OFF( void ) ; //LED關閉
void Set_USBClock( void ) ; //USB時鐘配置函數
void Enter_LowPowerMode( void ) ; //USB進入低功耗模式
void Leave_LowPowerMode( void ) ; //USB退出低功耗模式
void USB_Interrupts_Config( void ) ; //USB中斷配置
void USB_Port_Set( u8 enable ) ; //USB使能
void Get_SerialNum( void ) ; //獲取STM32的唯一ID
#endif
(9)hw_config.c文件重寫
#include "usb_lib.h"
#include "mass_mal.h"
#include "usb_desc.h"
#include "usb_pwr.h"
#include "usb_lib.h"
#include "usb_istr.h"
void USB_NotConfigured_LED()
{
}
void USB_Cable_Config( FunctionalState NewState )
{
}
void Led_RW_OFF()
{
}
void Led_RW_ON()
{
}
void USBWakeUp_IRQHandler()
{
EXTI->PR |= 1<<18 ; //清除USB喚醒中斷掛起位
}
void USB_LP_CAN1_RX0_IRQHandler()
{
USB_Istr() ;
}
void Set_USBClock()
{
RCC->CFGR &= ~( 1<<22 ) ; //USBclk=PLLclk/1.5=48Mhz
RCC->APB1ENR |= 1<<23 ; //USB時鐘使能
}
void Enter_LowPowerMode()
{
bDeviceState = SUSPENDED ;
}
void Leave_LowPowerMode()
{
DEVICE_INFO *pInfo = &Device_Info ;
if( pInfo->Current_Configuration!=0 )
bDeviceState=CONFIGURED ;
else
bDeviceState = ATTACHED ;
}
void USB_Interrupts_Config()
{
EXTI->IMR |= 1<<18 ; //開啟線18上的中斷
EXTI->RTSR |= 1<<18 ; //line 18上事件上升降沿觸發
NVIC_Init( 1, 0, USB_LP_CAN1_RX0_IRQn, 2 ) ; //組2優先級次之
NVIC_Init( 0, 0, USBWakeUp_IRQn, 2 ) ; //組2優先級最高
}
void USB_Port_Set( u8 enable )
{
RCC->APB2ENR |= 1<<2 ; //使能PORTA時鐘
if( enable )
_SetCNTR( _GetCNTR()&0xFFFFFFFD ) ; //退出斷電模式
else
{
_SetCNTR( _GetCNTR()|0x02 ) ; //斷電模式
GPIOA->CRH &= 0xFFF00FFF ;
GPIOA->CRH |= 0x00033000 ;
PAout( 12 ) = 0 ;
}
}
void IntToUnicode( u32 value, u8 *pbuf, u8 len )
{
u8 idx ;
for( idx=0; idx
}
void Get_SerialNum()
{
u32 Device_Serial0, Device_Serial1, Device_Serial2 ;
Device_Serial0 = *( u32* )0x1FFFF7E8 ;
Device_Serial1 = *( u32* )0x1FFFF7EC ;
Device_Serial2 = *( u32* )0x1FFFF7F0 ;
Device_Serial0 += Device_Serial2 ;
if( Device_Serial0!=0 )
{
IntToUnicode( Device_Serial0, &MASS_StringSerial[ 2 ] , 8 ) ;
IntToUnicode( Device_Serial1, &MASS_StringSerial[ 18 ], 4 ) ;
}
}
(10)platform_config.h文件重寫
#ifndef PLATFORM_CONFIG_H
#define PLATFORM_CONFIG_H
#include "sys.h"
#define USE_STM3210E_EVAL //當前使用的版本
#endif
(11)usb_pwr.c文件修改(改寫Suspend函數)
void Suspend(void)
{
uint32_t i=0 ;
uint16_t wCNTR ;
__IO uint32_t savePWR_CR=0 ;
wCNTR = _GetCNTR() ;
for( i=0; i<8; i++ )
EP[ i ] = _GetENDPOINT( i ) ;
wCNTR |= CNTR_RESETM ;
_SetCNTR( wCNTR ) ;
wCNTR |= CNTR_FRES ;
_SetCNTR( wCNTR ) ;
wCNTR &= ~CNTR_FRES ;
_SetCNTR( wCNTR ) ;
while( ( _GetISTR()&ISTR_RESET )==0 ) ;
_SetISTR( ( uint16_t )CLR_RESET ) ;
for( i=0; i<8; i++ )
_SetENDPOINT( i, EP[ i ] ) ;
wCNTR |= CNTR_FSUSP ;
_SetCNTR( wCNTR ) ;
wCNTR = _GetCNTR() ;
wCNTR |= CNTR_LPMODE ;
_SetCNTR( wCNTR ) ;
Enter_LowPowerMode() ;
}
(12)在sys.h文件中添加幾個重要的數據類型
typedef volatile uint8_t vu8;
#define __IO volatile
(13)stm32f103x.h文件中添加一個數據類型
typedef long long uint64_t ;
通過以上步驟,USB固件庫的移植就完成了,然后只需要調用固件庫的函數即可完成USB通信。
22.2.3 主函數編寫
#include "sys.h"
#include "delay.h"
#include "usart1.h"
#include "lcd.h"
#include "sdio_sdcard.h"
#include "w25q128.h"
#include "malloc.h"
#include "mass_mal.h"
#include "usb_lib.h"
#include "hw_config.h"
#include "usb_pwr.h"
#include "memory.h"
#include "usb_bot.h"
extern u8 Max_Lun ; //支持的磁盤個數
int main()
{
u8 USB_STA, Divece_STA, tct=0, offline_cnt=0;
STM32_Clock_Init( 9 ) ; //系統時鐘設置
SysTick_Init( 72 ) ; //延時初始化
USART1_Init( 72, 115200 ) ; //串口初始化為115200
LCD_Init() ; //初始化LCD
W25QXX_Init() ; //初始化W25Q128
my_mem_init( SRAMIN ) ; //初始化內部內存池
//初始化SD卡
if( SD_Init() )
Max_Lun = 0 ; //SD卡錯誤,則僅只有一個磁盤
//SD 卡正常
else
{
Mass_Memory_Size[ 1 ] = SDCardInfo.CardCapacity ; //得到SD卡容量(字節),當SD卡容量超過4G的時候,需要用到兩個u32來表示
Mass_Block_Size[ 1 ] = 512 ; //因為我們在Init里面設置了SD卡的操作字節為512個,所以這里一定是512個字節
Mass_Block_Count[ 1 ] = Mass_Memory_Size[ 1 ]/Mass_Block_Size[ 1 ] ;
}
Mass_Memory_Size[ 0 ] = 1024*1024*12 ; //前12M字節
Mass_Block_Size[ 0 ] = 512 ; //設置SPI FLASH的操作扇區大小為512
Mass_Block_Count[ 0 ] = Mass_Memory_Size[ 0 ]/Mass_Block_Size[ 0 ] ;
delay_ms( 1800 ) ;
USB_Port_Set( 0 ) ; //USB先斷開
delay_ms( 700 ) ;
USB_Port_Set( 1 ) ; //USB再次連接
LCD_ShowString( 30, 10, "USB Connecting..." ) ; //提示USB開始連接
Data_Buffer = mymalloc( SRAMIN, BULK_MAX_PACKET_SIZE*2*4 ) ; //為USB數據緩存區申請內存
Bulk_Data_Buff = mymalloc( SRAMIN, BULK_MAX_PACKET_SIZE ) ; //申請內存
//USB配置
USB_Interrupts_Config() ;
Set_USBClock() ;
USB_Init() ;
delay_ms( 1800 ) ;
while(1)
{
delay_ms( 1 ) ;
//狀態改變了
if( USB_STA!=USB_STATUS_REG )
{
LCD_ShowString( 30, 30, " " ) ; //清除顯示
//正在寫
if( USB_STATUS_REG&0x01 )
LCD_ShowString( 30, 30, "USB Writing..." ) ; //提示USB正在寫入數據
//正在讀
if( USB_STATUS_REG&0x02 )
LCD_ShowString( 30, 30, "USB Reading..." ) ; //提示USB正在讀出數據
if( USB_STATUS_REG&0x04 )
LCD_ShowString( 30, 50, "USB Write Err " ) ; //提示寫入錯誤
else
LCD_ShowString( 30, 50, " " ) ; //清除顯示
if( USB_STATUS_REG&0x08 )
LCD_ShowString( 30, 80, "USB Read Err " ) ; //提示讀出錯誤
else
LCD_ShowString( 30, 80, " " ) ; //清除顯示
USB_STA = USB_STATUS_REG ; //記錄最后的狀態
}
//獲取USB連接狀態
if( Divece_STA!=bDeviceState )
{
if( bDeviceState==CONFIGURED )
LCD_ShowString( 30, 10, "USB Connected " ) ; //提示USB連接已經建立
else
LCD_ShowString( 30, 10, "USB DisConnected " ) ; //提示USB被拔出了
Divece_STA = bDeviceState ;
}
tct ++ ;
if( tct==200 )
{
tct = 0 ;
if( USB_STATUS_REG&0x10 )
{
offline_cnt = 0 ; //USB連接了,則清除offline計數器
bDeviceState = CONFIGURED ;
}
//沒有得到輪詢
else
{
offline_cnt ++ ;
if( offline_cnt>10 )
bDeviceState = UNCONNECTED ; //2s內沒收到在線標記,代表USB被拔出了
}
USB_STATUS_REG = 0 ;
}
}
}
-
usb
+關注
關注
60文章
7989瀏覽量
266275 -
PC
+關注
關注
9文章
2105瀏覽量
154742 -
接口技術
+關注
關注
1文章
276瀏覽量
41428
發布評論請先 登錄
相關推薦
![](https://file1.elecfans.com/web2/M00/85/8E/wKgaomRmNDKAVLgfAAAUoyXusRg734.png)
![](https://file1.elecfans.com/web2/M00/85/90/wKgZomRmNHqAREBxAAHopn17Kjw480.png)
![](https://file1.elecfans.com/web2/M00/85/8F/wKgaomRmNHqAJJUHAAG3BAaNEn4696.png)
![](https://file1.elecfans.com/web2/M00/85/8F/wKgaomRmNHuAbyR1AAH98bpX6to083.png)
![](https://file1.elecfans.com/web2/M00/85/90/wKgZomRmNHuAX-IwAAGa-d-6rlc219.png)
stm32的USB讀卡器實驗源碼
「正點原子STM32Mini板資料連載」第三十九章 USB 讀卡器實驗
「正點原子NANO STM32開發板資料連載」第34章 USB 讀卡器實驗
「正點原子NANO STM32F103開發板資料連載」第三十二章 USB 讀卡器實驗
USB讀卡器實驗
STM32入門學習筆記之USB讀卡器實驗1
![STM32入門學習筆記之<b class='flag-5'>USB</b><b class='flag-5'>讀卡器</b><b class='flag-5'>實驗</b>1](https://file.elecfans.com/web2/M00/91/88/poYBAGPt36iAFzLrAAE9938xwgM472.jpg)
STM32入門學習筆記之USB讀卡器實驗2
![STM32入門學習筆記之<b class='flag-5'>USB</b><b class='flag-5'>讀卡器</b><b class='flag-5'>實驗</b>2](https://file.elecfans.com/web2/M00/92/0B/pYYBAGPt36iAB5p5AABzRe9XXrw541.jpg)
評論