大家好,我在使用SSI0模块和DMA模块时碰到了一个很费解的问题,我在连续读取SD卡多个扇区时发现每当读取奇数个扇区时最后一个扇区的数据不完整,只能读到200多字节数据,后面的数据全为0,但是读偶数个扇区一切正常。并且我尝试使用单扇区读取函数连续读奇数个扇区却没问题,于是我把这个单扇区读取函数嵌入到多扇区读取函数里面问题又出现了,两个函数最大的区别就是接收的目的地址,我觉得可能DMA的RX通道在计算目的地址出现了问题,因为它的目的地址是连续的计算出来的:&pbuffer[i*512]。但是我尝试了多个办法都解决不了,希望有人能可以帮我看看,下面是一些主要函数的程序,谢谢大家了!
void SD_SPI_Init()
{
HWREG(GPIO_PORTA_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY; //PA3解锁操作
HWREG(GPIO_PORTA_BASE + GPIO_O_CR) |= GPIO_PIN_3;
GPIOPinTypeGPIOOutput(GPIO_PORTA_BASE, GPIO_PIN_3); //PA3选为SSI0_CS引脚
HWREG(GPIO_PORTA_BASE + GPIO_O_LOCK) = GPIO_LOCK_LOCKED;
GPIOPinWrite(GPIO_PORTA_BASE, GPIO_PIN_3, GPIO_PIN_3); //SSI0_CS默认高电平
GPIOPinConfigure(GPIO_PA2_SSI0CLK); //PA2选为SSI0_CLK引脚
GPIOPinConfigure(GPIO_PA4_SSI0RX); //PA4选为SSI0_RX引脚
GPIOPinConfigure(GPIO_PA5_SSI0TX); //PA5选为SSI0_TX引脚
GPIOPinTypeSSI(GPIO_PORTA_BASE, GPIO_PIN_2|GPIO_PIN_4|GPIO_PIN_5); //SSI0端口功能使能
SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI0); //使能SSI0模块
SSIClockSourceSet(SSI0_BASE, SSI_CLOCK_SYSTEM); //SSI0时钟选择系统主时钟
SSIConfigSetExpClk(SSI0_BASE, SysCtlClockGet(), //SSI0作为主机,工作模式为0
SSI_FRF_MOTO_MODE_3, SSI_MODE_MASTER, 20000000, 8); //Fclk为20Mhz,数据帧8位
SSIEnable(SSI0_BASE); //使能SSI0
#ifdef SD_CARD_USE_DMA
uDMAChannelAssign(UDMA_CH11_SSI0TX); //配置Channel11_SSI0TX通道
uDMAChannelAssign(UDMA_CH10_SSI0RX);
uDMAChannelAttributeEnable(UDMA_CH11_SSI0_TX, UDMA_ATTR_USEBURST //通道11突发模式,高优先级
|UDMA_ATTR_HIGH_PRIORITY);
uDMAChannelAttributeEnable(UDMA_CH10_SSI0_RX, UDMA_ATTR_USEBURST //通道10突发模式,高优先级
|UDMA_ATTR_HIGH_PRIORITY);
uDMAChannelControlSet(UDMA_CH11_SSI0_TX | UDMA_PRI_SELECT, //配置Channel11_PRI结构,数据单位字节
UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_4); //原地址增加,目的地址不变,一次传输8个item
uDMAChannelControlSet(UDMA_CH10_SSI0_RX | UDMA_PRI_SELECT, //配置Channel10_PRI结构,数据单位字节
UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_8 | UDMA_ARB_4); //原地址不变,目的地址增加,一次传输8个item
#endif
}
uint8_t SD_Read_Sector(uint32_t uiAddr, uint8_t *pbuffer)
{
uint8_t ucGet, ucTry;
uint8_t pCMD17[]={0x51,0x00,0x00,0x00,0x00,0x01}; //CMD17的字节序列
if(!SD_Addr_Mode)
{
uiAddr<<=9; //sector = sector * 512 将块地址(扇区地址)转为字节地址
}
pCMD17[1]=uiAddr>>24; //将字节地址写入到CMD17字节序列中
pCMD17[2]=uiAddr>>16;
pCMD17[3]=uiAddr>>8;
pCMD17[4]=uiAddr;
ucTry=0;
do
{
ucGet=SD_Write_Cmd(pCMD17); //写入CMD17
ucTry++;
if(ucTry==TRY_TIME)
{
return(READ_BLOCK_ERROR); //读块失败
}
}while(ucGet!=0);
while(SD_Read_Byte()!=0xFE); //一直读,当读到0xFE时,说明后面的是512字节的数据了
#ifdef SD_CARD_USE_DMA
uint8_t uiDMA_Temp=0xFF;
uDMAChannelControlSet(UDMA_CH11_SSI0_TX | UDMA_PRI_SELECT, //配置Channel11_PRI结构
UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE | UDMA_ARB_4); //原地址不变,目的地址不变
uDMAChannelTransferSet(UDMA_CH11_SSI0_TX | UDMA_PRI_SELECT, //基本传输模式,原地址和目的地址指针
UDMA_MODE_BASIC, &uiDMA_Temp, (void *)(SSI0_BASE + SSI_O_DR), 512); //传输的item为512
uDMAChannelTransferSet(UDMA_CH10_SSI0_RX | UDMA_PRI_SELECT, //基本传输模式,原地址和目的地址指针
UDMA_MODE_BASIC, (void *)(SSI0_BASE + SSI_O_DR), pbuffer, 512); //传输的item为512
SSIDMAEnable(SSI0_BASE, SSI_DMA_TX|SSI_DMA_RX);
uDMAChannelEnable(UDMA_CH10_SSI0_RX); //使能一次item传输
uDMAChannelEnable(UDMA_CH11_SSI0_TX); //使能一次item传输
while(uDMAChannelIsEnabled(UDMA_CH10_SSI0_RX)); //等待一次DMA传输完成
SSIDMADisable(SSI0_BASE, SSI_DMA_TX|SSI_DMA_RX);
uDMAChannelControlSet(UDMA_CH11_SSI0_TX | UDMA_PRI_SELECT, //配置Channel11_PRI结构
UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_4); //原地址增加,目的地址不变
#else
uint16_t j;
for(j=0; j<512; j++)
{
pbuffer[j] = SD_Read_Byte();
}
#endif
SD_Read_Byte();
SD_Read_Byte(); //读取两个字节的CRC校验码,不用关心它们
SD_CS_SET;
SD_Write_Byte(0xFF); //按照SD卡的操作时序在这里补8个时钟
return(0); //返回0,说明写扇区操作成功
}
uint8_t SD_Read_nSector(uint32_t uinSec, uint32_t uiAddr, uint8_t *pbuffer)
{
uint16_t i;
uint8_t ucGet, ucTry;
uint8_t pCMD18[6]={0x52,0x00,0x00,0x00,0x00,0x01}; //CMD18的字节序列
uint8_t pCMD12[6]={0x1C,0x00,0x00,0x00,0x00,0x01}; //CMD12,强制停止命令
if(!SD_Addr_Mode)
{
uiAddr<<=9; //sector = sector * 512 将块地址(扇区地址)转为字节地址
}
pCMD18[1]=uiAddr>>24; //将字节地址写入到CMD17字节序列中
pCMD18[2]=uiAddr>>16;
pCMD18[3]=uiAddr>>8;
pCMD18[4]=uiAddr;
ucTry=0;
do
{
ucGet=SD_Write_Cmd(pCMD18); //写入CMD18
ucTry++;
if(ucTry==TRY_TIME)
{
return(READ_CMD18_ERROR); //写入CMD18失败
}
}while(ucGet!=0);
for(i=0; i<uinSec; i++)
{
while(SD_Read_Byte()!=0xFE); //一直读,当读到0xfe时,说明后面的是512字节的数据了
#ifdef SD_CARD_USE_DMA
uint8_t uiDMA_Temp=0xFF;
uDMAChannelControlSet(UDMA_CH11_SSI0_TX | UDMA_PRI_SELECT, //配置Channel11_PRI结构
UDMA_SIZE_8 | UDMA_SRC_INC_NONE | UDMA_DST_INC_NONE | UDMA_ARB_4); //原地址不变,目的地址不变
uDMAChannelTransferSet(UDMA_CH11_SSI0_TX | UDMA_PRI_SELECT, //基本传输模式,原地址和目的地址指针
UDMA_MODE_BASIC, &uiDMA_Temp, (void *)(SSI0_BASE + SSI_O_DR), 512); //传输的item为512
uDMAChannelTransferSet(UDMA_CH10_SSI0_RX | UDMA_PRI_SELECT, //基本传输模式,原地址和目的地址指针
UDMA_MODE_BASIC, (void *)(SSI0_BASE + SSI_O_DR), &pbuffer[i*512], 512);//传输的item为512
SSIDMAEnable(SSI0_BASE, SSI_DMA_TX|SSI_DMA_RX);
uDMAChannelEnable(UDMA_CH10_SSI0_RX); //使能一次item传输
uDMAChannelEnable(UDMA_CH11_SSI0_TX); //使能一次item传输
while(uDMAChannelIsEnabled(UDMA_CH10_SSI0_RX)); //等待一次DMA传输完成
SSIDMADisable(SSI0_BASE, SSI_DMA_TX|SSI_DMA_RX);
uDMAChannelControlSet(UDMA_CH11_SSI0_TX | UDMA_PRI_SELECT, //配置Channel11_PRI结构
UDMA_SIZE_8 | UDMA_SRC_INC_8 | UDMA_DST_INC_NONE | UDMA_ARB_4); //原地址增加,目的地址不变
#else
uint16_t j;
for(j=0; j<512; j++)
{
pbuffer[i*512+j] = SD_Read_Byte();
}
#endif
SD_Read_Byte();
SD_Read_Byte();//读取两个字节的CRC校验码,不用关心它们
}
SD_Write_Cmd(pCMD12); //写入CMD12命令,停止数据读取
SD_CS_SET;
SD_Write_Byte(0xFF); //按照SD卡的操作时序在这里补8个时钟
return(0); //返回0,说明写扇区操作成功
}
xyz549040622:
你这个帖子的信息量太大,手头没有SD的板子,无法测试。但是TIVA开发包中有SD卡的例子,你可以参考看看。例子在dk-tm4c123g这个文件夹下。
xyz549040622:
回复 JinPing Zhang2:
同意你的判断。那么你可以暂时不用DMA操作,和SD卡通讯OK后,再去找DMA的问题。这样也可以排除SD卡部分的原因。