之前做畢設買了CH375B模塊,一直沒好好用過。現在想把這個模塊用起來。程序參考的振南的51例程《U盤扇區讀寫[IO方式]》 和正點原子的例程模板,只要把最底層的扇區讀寫測試通過,znFAT文件系統就可以用了。程序用的並口通信控制。先說端口配置:8位數據端口用的GPIOC的低8位,寫選通WR、讀選通RD、片選 CS、命令口和數據口地址選擇A0和中斷INT用的GPIOB端口。端口定義如下:
#define GPIO_CH375_Data GPIOC //數據端口
#define DATA_MODE_IN GPIO_CH375_Data->CRL=0x44444444; // Floating IN
#define DATA_MODE_OUT GPIO_CH375_Data->CRL=0x33333333; // PP_OUT 50MHZ
#define GPIO_CH375_CTL GPIOB //CH375控制端口
#define WR PBout(11)
#define RD PBout(10)
#define CS PBout(9)
#define A0 PBout(8)
#define INT PBin(12)
端口初始化代碼如下:
void CH375_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_GPIOC, ENABLE); //
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8 | GPIO_Pin_9 | GPIO_Pin_10 |GPIO_Pin_11 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure); //
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12; // INT中斷
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;// GPIO_Mode_IPU;
GPIO_Init(GPIOB, &GPIO_InitStructure);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 |GPIO_Pin_3|
GPIO_Pin_4|GPIO_Pin_5|GPIO_Pin_6|GPIO_Pin_7 ; //GPIOC數據端口
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOC, &GPIO_InitStructure); //
}
//寫命令
void CH375_WR_CMD( u8 cmd )
{
u16 TempData=0;
DATA_MODE_OUT; //設置成數據輸出
TempData=GPIO_ReadOutputData(GPIO_CH375_Data); //讀取數據口數據,保護高8位
TempData &= 0xFF00; //低8位清0,保留高8位狀態
TempData |= (u16)cmd; //寫命令
delay_us(10); //延時
CS=0; //打開片選
// delay_us(10);
A0=1; //命令模式
// delay_us(10);
GPIO_Write(GPIO_CH375_Data, TempData); //向CH375寫命令碼
//delay_us(10);
WR=0; //打開寫使能
delay_us(10); //
WR=1; //關閉寫使能
//delay_us(10);
CS=1; //關閉片選
// delay_us(10);
A0=1; //恢復A0爲高電平
delay_us(200); //延時
}
//寫數據
void CH375_WR_DAT( u8 dat )
{
u16 TempData=0;
DATA_MODE_OUT; //數據端口設置成輸出,發送數據給CH375
TempData=GPIO_ReadOutputData(GPIO_CH375_Data);//讀數據端口數據,保護高8位
TempData &= 0xFF00; //低8位清0
TempData |= (u16)dat; //寫數據
delay_us(10); //
CS=0; //
//delay_us(10);
A0=0; //
//delay_us(10);
GPIO_Write(GPIO_CH375_Data, TempData); //
//delay_us(10);
WR=0; //
delay_us(10);
WR=1; //
// delay_us(10);
CS=1; //
//delay_us(10);
A0=1; //
delay_us(200); //
}
//讀取數據
u8 CH375_RD_DAT( void )
{
u8 PortData;
DATA_MODE_IN; //
delay_us(10); //
CS=0; //
// delay_us(10);
A0=0; //
//delay_us(10);
RD=0; //
// delay_us(10);
PortData = (u8)( GPIO_ReadInputData(GPIO_CH375_Data) ); //
delay_us(10);
RD=1; //
//delay_us(10);
CS=1; //
//delay_us(10);
A0=1; //
//delay_us(10);
return PortData; //
}
//等待中斷
u8 WaitInterrupt( void )
{
delay_us(10);
INT=1; //IO口作輸入先置高
while(INT); //等待中斷,低電平有效
delay_us(10);
CH375_WR_CMD( CMD_GET_STATUS ); //產生操作完成中斷,獲取中斷狀態,收到GET_STATUS命令到INT#引腳撤消中斷最大延時3us
delay_us(10);
return( CH375_RD_DAT( ) );
}
寫好讀寫數據命令函數後就是CH375初始化和磁盤初始化:
//CH375芯片初始化,設置CH375爲USB主機模式,成功返回0,失敗返回1
u8 CH375Init( void )
{
u8 i;
u8 Read_Data=0;
CH375_WR_CMD( CMD_CHECK_EXIST ); //測試工作狀態
CH375_WR_DAT( 0x55 ); // 測試數據
Read_Data = CH375_RD_DAT( ); // 返回的數據應是測試數據取反,如發送0x55,返回0xAA
if ( Read_Data != 0xaa ) //CH375出錯
{
for ( i = 100; i != 0; i -- )
{
CH375_WR_CMD( CMD_RESET_ALL );
delay_ms(50); //RESET_ALL命令的執行時間最大爲40ms
CH375_WR_CMD( CMD_CHECK_EXIST ); // 測試工作狀態
CH375_WR_DAT( 0x55 ); // 測試數據
Read_Data = CH375_RD_DAT( );
if ( Read_Data == 0xaa ) //讀取到的數據是輸入數據按位取反
break;
}
}
CH375_WR_CMD( CMD_SET_USB_MODE ); // 設置USB工作模式
delay_us(30);//設置USB工作模式爲等待20us
CH375_WR_DAT( 6 ); //模式代碼,自動檢測USB設備連接
for ( i = 0xff; i != 0; i -- ) // 等待操作成功,通常要等待10uS-20uS //
{
delay_us(20);
if ( CH375_RD_DAT( ) == CMD_RET_SUCCESS ) break; //操作成功,退出循環
}
if ( i != 0 ) return( 0 ); // 操作成功,返回0
else return( 1 ); // CH375初始化出錯,如芯片型號出錯或處於串口方式或不支持,返回1
}
//磁盤初始化,成功返回0,失敗返回1
unsigned char CH375_InitDisk(void)
{
unsigned char status,i,j=0;
//delay_us(100);
status=WaitInterrupt();
delay_us(100);
if(status==USB_INT_DISCONNECT) return 1; //USB設備斷開
while(1)
{
CH375_WR_CMD(CMD_DISK_INIT); //初始化USB存儲器
delay_us(100);
status=WaitInterrupt(); //等待中斷並獲取中斷狀態
delay_us(100);
if(status==USB_INT_SUCCESS)
break;
}
while(1) //以下代碼均源自於沁恆的官方優盤初始化函數,借用它可提高對優盤的兼容性
{
j++;
CH375_WR_CMD(CMD_DISK_SIZE); //獲取優盤容量
delay_us(100);
status=WaitInterrupt(); //等待中斷並獲取中斷狀態
delay_us(100);
if(status==USB_INT_SUCCESS)
break;
else
{
//Delay(1000);
delay_us(100);
CH375_WR_CMD(CMD_DISK_R_SENSE); //主機方式:檢查USB存儲器錯誤
delay_us(100);
status=WaitInterrupt(); //等待中斷並獲取中斷狀態
delay_us(100);
if(status==USB_INT_SUCCESS)
continue; //初始化成功跳出while(1)
else
return 1; //初始化失敗,返回1
}
if(j==5)
return 1; //初始化失敗,返回1
}
for(i=0;i!=5;i++)
{
//delay_us(100);
CH375_WR_CMD( CMD_DISK_READY ); //檢查USB存儲設備的錯誤
delay_us(100);
status=WaitInterrupt(); //等待中斷並獲取中斷狀態
delay_us(100);
if(status==USB_INT_SUCCESS)
return 0; //優盤已經成功初始化,返回0
}
return 1; //優盤初始化失敗,返回1
}
後面就是main函數了,測試扇區讀寫功能:
#define ADDR 100//6600000//100 //要操作的優盤物理扇區地址 用winhex打開物理磁盤可查看扇區地址數據
u8 flag1=0;
u8 Write_pbuf[512]; //發送數據緩衝區
u8 Read_pbuf[2048]; //接收數據緩衝區
u8 status=0xff; //初始化狀態標誌
//注意:單片機要先上電,再插入優盤
int main(void)
{
u16 i=0;
delay_init(); //延時函數初始化
LED_Init();
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //設置NVIC中斷分組2:2位搶佔優先級,2位響應優先級
uart_init(9600); //串口初始化波特率9600
printf("串口設置完畢\r\n");
delay_ms(100); //內部電源上電的復位時間最大爲40ms,這裏延時防止出現CH375初始化失敗
CH375_GPIO_Init(); //
CH375_WR_CMD(CMD_GET_IC_VER); // 獲取芯片及固件版本
status=CH375_RD_DAT(); //
printf("芯片版本爲:%#x\r\n",status);
status=CH375Init(); //初始化CH375芯片,成功返回0,失敗返回1
printf("CH375芯片初始化值:%#x\r\n",status);
CH375_WR_CMD(CMD_DISK_MAX_LUN); //獲取USB存儲設備的最大邏輯單元號
status=CH375_RD_DAT(); // 最大邏輯單元號
printf("USB存儲設備的最大邏輯單元號爲:%#x\r\n",status);
//status=InitDisk(); //初始化優盤,成功返回0,失敗返回1
//注意:單片機要先上電,再插入優盤
status=CH375_InitDisk(); //上電後再插入優盤 金士頓8G優盤 東莞16G 3.0優盤測試通過
printf("U盤初始化值:%#x\r\n",status);
status=Get_CH375DiskSize();//打印磁盤容量,單位MByte
for(i=0;i<512;i++)
Write_pbuf[i]=i;//0x55;// //向數據緩衝區中寫入0-255 0-255 共512個字節
printf("向緩衝區中裝入完畢\r\n");
//CH375WriteSector(ADDR,Write_pbuf);//將數據緩衝區中的512個字節數據寫入優盤的第ADDR個扇區
CH375_WriteDisk(Write_pbuf,ADDR,4); //連續寫多個扇區
printf("寫U盤扇區完畢\r\n");
for(i=0;i<2048;i++) //清空接收數據緩衝區
{
Read_pbuf[i]=0;
}
printf("清空接收緩衝區完畢\r\n");
//CH375ReadSector(ADDR+1,Read_pbuf);//從優盤的第ADDR個扇區中讀取512個字節數據到數據緩衝區
CH375_ReadDisk(Read_pbuf,ADDR,4); //連續讀取多個扇區數據
//查看讀取到的幾個扇區數據,只取一小部分
for(i=0;i<10;i++)
{
printf("讀取到的扇區數據爲:i=%d Read_pbuf[i]=%#x\r\n",i,Read_pbuf[i]);
}
for(i=520;i<530;i++)
{
printf("讀取到的扇區數據爲:i=%d Read_pbuf[i]=%#x\r\n",i,Read_pbuf[i]);
}
for(i=1030;i<1040;i++)
{
printf("讀取到的扇區數據爲:i=%d Read_pbuf[i]=%#x\r\n",i,Read_pbuf[i]);
}
for(i=1600;i<1610;i++)
{
printf("讀取到的扇區數據爲:i=%d Read_pbuf[i]=%#x\r\n",i,Read_pbuf[i]);
}
printf("讀取優盤扇區完畢\r\n");
for(i=0;i<512;i++)
{
if(Write_pbuf[i]!=Read_pbuf[i]) //對讀取的數據進行匹配
{
flag1=1; //匹配失敗,flag1=1
break;
}
}
printf("匹配完畢\r\n");
for(i=0;i<512;i++) //清空發送數據緩衝區
{
Write_pbuf[i]=0;
//printf("扇區數據爲:%#x\n",Write_pbuf[i]);
}
if(flag1)
{
LED=1; //發光LED滅
printf("優盤扇區讀寫測試失敗\r\n"); //數據不吻合
}
else
{
LED=0; //發光LED點亮
printf("優盤扇區讀寫測試成功\r\n"); //數據吻合
}
printf("------------------------------------\r\n");
while(1){
}
}
測試結果如下:
金士頓8G優盤串口內容:
使用winhex打開金士頓8G優盤查看數據和容量如下:
總容量=總扇區數*每扇區字節數=15131636*512=7747397632字節=7388.494140625MByte
之前用程序計算總容量的時候沒注意到變量位數,導致計算後的總字節數超過定義的32位變量,輸出的結果有誤,修改計算過程後計算結果正常了。
到此,用STM32F103RC和CH375模塊讀寫優盤扇區基本實現了,後面就可以用znFAT或FATFS在優盤上讀寫文件了。
詳細例程代碼見我的下載https://download.csdn.net/download/u013072995/11223269