本次主要实现SPI接口的外接TF卡实现FATFS文件系统。
首先是SPI接口初始化。使用SPI1外设,对应引脚如下:
* PA4 <===========> SCS
* PA5 <===========> SCK
* PA6 <===========> DI/MISO
* PA7 <===========> DO/MOSI
SPI初始化如下:
/**
* [url=home.php?mod=space&uid=830701]@name[/url] drv_spi_init
* [url=home.php?mod=space&uid=2666770]@Brief[/url] spi 外设初始化
* [url=home.php?mod=space&uid=3142012]@param[/url] None
* [url=home.php?mod=space&uid=1141835]@Return[/url] None
*/
void drv_spi_init(void)
{
GPIO_InitTypeDef GPIO_InitStructure={0};
SPI_InitTypeDef SPI_InitStructure={0};
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_SPI1, ENABLE );
//CS
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//SCK
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &GPIO_InitStructure );
//MISO
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init( GPIOA, &GPIO_InitStructure );
//MOSI
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &GPIO_InitStructure );
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_32;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init( SPI1, &SPI_InitStructure );
SPI_Cmd( SPI1, ENABLE );
}
SPI发送接收数据
uint8_t spi_read_write(uint8_t _data)
{
/*!< Loop while DAT register in not emplty */
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_TXE) == RESET) ;
/*!< Send byte through the SPI1 peripheral */
SPI_I2S_SendData(SPI1, _data);
/*!< Wait to receive a byte */
while (SPI_I2S_GetFlagStatus(SPI1, SPI_I2S_FLAG_RXNE) == RESET) ;
/*!< Return the byte read from the SPI bus */
return SPI_I2S_ReceiveData(SPI1);
}
接下来是实现TF的初始化和读写数据接口。主要参考以前移植的代码,实现很容易。
int MSD_Init(void)
{
uint8_t r1;
uint8_t buff[6] = {0};
uint16_t retry;
MSD_SPI_Configuration();
/* Check , if no card insert */
// if( MSD_Check_Insert() )
// {
// return -1;
// }
/* Power on and delay some times */
for(retry=0; retry<0x100; retry++)
{
_card_power_on();
}
/* Satrt send 74 clocks at least */
for(retry=0; retry<10; retry++)
{
spi_read_write(DUMMY_BYTE);
}
/* Start send CMD0 till return 0x01 means in IDLE state */
for(retry=0; retry<0xFFF; retry++)
{
r1 = _send_command(CMD0, 0, 0x95);
if(r1 == 0x01)
{
retry = 0;
break;
}
}
/* Timeout return */
if(retry == 0xFFF)
{
#ifdef PRINT_INFO
DEBUG_OUT("Reset card into IDLE state failed!\r\n");
#endif
return 1;
}
/* Get the card type, version */
r1 = _send_command_hold(CMD8, 0x1AA, 0x87);
/* r1=0x05 -> V1.0 */
if(r1 == 0x05)
{
CardInfo.CardType = CARDTYPE_SDV1;
/* End of CMD8, chip disable and dummy byte */
_card_disable();
spi_read_write(DUMMY_BYTE);
/* SD1.0/MMC start initialize */
/* Send CMD55+ACMD41, No-response is a MMC card, otherwise is a SD1.0 card */
for(retry=0; retry<0xFFF; retry++)
{
r1 = _send_command(CMD55, 0, 0); /* should be return 0x01 */
if(r1 != 0x01)
{
#ifdef PRINT_INFO
DEBUG_OUT("Send CMD55 should return 0x01, response=0x%02x\r\n", r1);
#endif
return r1;
}
r1 = _send_command(ACMD41, 0, 0); /* should be return 0x00 */
if(r1 == 0x00)
{
retry = 0;
break;
}
}
/* MMC card initialize start */
if(retry == 0xFFF)
{
for(retry=0; retry<0xFFF; retry++)
{
r1 = _send_command(CMD1, 0, 0); /* should be return 0x00 */
if(r1 == 0x00)
{
retry = 0;
break;
}
}
/* Timeout return */
if(retry == 0xFFF)
{
#ifdef PRINT_INFO
DEBUG_OUT("Send CMD1 should return 0x00, response=0x%02x\r\n", r1);
#endif
return 2;
}
CardInfo.CardType = CARDTYPE_MMC;
#ifdef PRINT_INFO
DEBUG_OUT("Card Type 111 : MMC\r\n");
#endif
}
/* SD1.0 card detected, print information */
#ifdef PRINT_INFO
else
{
DEBUG_OUT("Card Type 111 : SD V1\r\n");
}
#endif
/* Set spi speed high */
_card_speed_high();
/* CRC disable */
r1 = _send_command(CMD59, 0, 0x01);
if(r1 != 0x00)
{
#ifdef PRINT_INFO
DEBUG_OUT("Send CMD59 should return 0x00, response=0x%02x\r\n", r1);
#endif
return r1; /* response error, return r1 */
}
/* Set the block size */
r1 = _send_command(CMD16, MSD_BLOCKSIZE, 0xFF);
if(r1 != 0x00)
{
#ifdef PRINT_INFO
DEBUG_OUT("Send CMD16 should return 0x00, response=0x%02x\r\n", r1);
#endif
return r1; /* response error, return r1 */
}
}
/* r1=0x01 -> V2.x, read OCR register, check version */
else if(r1 == 0x01)
{
/* 4Bytes returned after CMD8 sent */
buff[0] = spi_read_write(DUMMY_BYTE); /* should be 0x00 */
buff[1] = spi_read_write(DUMMY_BYTE); /* should be 0x00 */
buff[2] = spi_read_write(DUMMY_BYTE); /* should be 0x01 */
buff[3] = spi_read_write(DUMMY_BYTE); /* should be 0xAA */
/* End of CMD8, chip disable and dummy byte */
_card_disable();
spi_read_write(DUMMY_BYTE);
/* Check voltage range be 2.7-3.6V */
if(buff[2]==0x01 && buff[3]==0xAA)
{
for(retry=0; retry<0xFFF; retry++)
{
r1 = _send_command(CMD55, 0, 0); /* should be return 0x01 */
if(r1!=0x01)
{
#ifdef PRINT_INFO
DEBUG_OUT("Send CMD55 should return 0x01, response=0x%02x\r\n", r1);
#endif
return r1;
}
r1 = _send_command(ACMD41, 0x40000000, 0); /* should be return 0x00 */
if(r1 == 0x00)
{
retry = 0;
break;
}
}
/* Timeout return */
if(retry == 0xFFF)
{
#ifdef PRINT_INFO
DEBUG_OUT("Send ACMD41 should return 0x00, response=0x%02x\r\n", r1);
#endif
return 3;
}
/* Read OCR by CMD58 */
r1 = _send_command_hold(CMD58, 0, 0);
if(r1!=0x00)
{
#ifdef PRINT_INFO
DEBUG_OUT("Send CMD58 should return 0x00, response=0x%02x\r\n", r1);
#endif
return r1; /* response error, return r1 */
}
buff[0] = spi_read_write(DUMMY_BYTE);
buff[1] = spi_read_write(DUMMY_BYTE);
buff[2] = spi_read_write(DUMMY_BYTE);
buff[3] = spi_read_write(DUMMY_BYTE);
/* End of CMD58, chip disable and dummy byte */
_card_disable();
spi_read_write(DUMMY_BYTE);
/* OCR -> CCS(bit30) 1: SDV2HC 0: SDV2 */
if(buff[0] & 0x40)
{
CardInfo.CardType = CARDTYPE_SDV2HC;
#ifdef PRINT_INFO
DEBUG_OUT("Card Type 222 : SD V2HC\r\n");
#endif
}
else
{
CardInfo.CardType = CARDTYPE_SDV2;
#ifdef PRINT_INFO
DEBUG_OUT("Card Type 222 : SD V2\r\n");
#endif
}
/* Set spi speed high */
_card_speed_high();
}
}
return 0;
}
/*******************************************************************************
* Function Name : _read_buffer
* Description : None
* Input : - *buff:
* - len:
* - release:
* Output : None
* Return : 0:NO_ERR; TRUE: Error
* Attention : None
*******************************************************************************/
int _read_buffer(uint8_t *buff, uint16_t len, uint8_t release)
{
uint8_t r1;
uint16_t retry;
/* Card enable, Prepare to read */
_card_enable();
/* Wait start-token 0xFE */
for(retry=0; retry<2000; retry++)
{
r1 = spi_read_write(DUMMY_BYTE);
if(r1 == 0xFE)
{
retry = 0;
break;
}
}
/* Timeout return */
if(retry == 2000)
{
#ifdef PRINT_INFO
DEBUG_OUT("_read_buffer Timeout return.\n");
#endif
_card_disable();
return 1;
}
/* Start reading */
for(retry=0; retry<len; retry++)
{
*(buff+retry) = spi_read_write(DUMMY_BYTE);
}
/* 2bytes dummy CRC */
spi_read_write(DUMMY_BYTE);
spi_read_write(DUMMY_BYTE);
/* chip disable and dummy byte */
if(release)
{
_card_disable();
spi_read_write(DUMMY_BYTE);
}
return 0;
}
/*******************************************************************************
* Function Name : MSD_ReadSingleBlock
* Description : None
* Input : - sector:
* - buffer:
* Output : None
* Return : None
* Attention : None
*******************************************************************************/
int MSD_ReadSingleBlock(uint32_t sector, uint8_t *buffer)
{
uint8_t r1;
/* if ver = SD2.0 HC, sector need <<9 */
if(CardInfo.CardType != CARDTYPE_SDV2HC)
{
sector = sector<<9;
}
/* Send CMD17 : Read single block command */
r1 = _send_command_hold(CMD17, sector, 0);
if(r1 != 0x00)
{
return 1;
}
/* Start read and return the result */
r1 = _read_buffer(buffer, MSD_BLOCKSIZE, RELEASE); // HOLD
/* Send stop data transmit command - CMD12 */
_send_command(CMD12, 0, 0);
return r1;
}
/*******************************************************************************
* Function Name : MSD_ReadMultiBlock
* Description : None
* Input : - sector:
* - buffer:
* - NbrOfSector:
* Output : None
* Return : None
* Attention : None
*******************************************************************************/
int MSD_ReadMultiBlock(uint32_t sector, uint8_t *buffer, uint32_t NbrOfSector)
{
uint8_t r1;
uint32_t i;
/* if ver = SD2.0 HC, sector need <<9 */
if(CardInfo.CardType != CARDTYPE_SDV2HC)
{
sector = sector<<9;
}
/* Send CMD18 : Read multi block command */
r1 = _send_command_hold(CMD18, sector, 0);
if(r1 != 0x00)
{
return 1;
}
/* Start read */
for(i=0; i<NbrOfSector; i++)
{
if(_read_buffer(buffer+i*MSD_BLOCKSIZE, MSD_BLOCKSIZE, HOLD))
{
/* Send stop data transmit command - CMD12 */
_send_command(CMD12, 0, 0);
/* chip disable and dummy byte */
_card_disable();
return 2;
}
}
/* Send stop data transmit command - CMD12 */
_send_command(CMD12, 0, 0);
/* chip disable and dummy byte */
_card_disable();
spi_read_write(DUMMY_BYTE);
return 0;
}
/*******************************************************************************
* Function Name : MSD_WriteSingleBlock
* Description : None
* Input : - sector:
* - buffer:
* Output : None
* Return : None
* Attention : None
*******************************************************************************/
int MSD_WriteSingleBlock(uint32_t sector, uint8_t *buffer)
{
uint8_t r1;
uint16_t i;
uint32_t retry;
/* if ver = SD2.0 HC, sector need <<9 */
if(CardInfo.CardType != CARDTYPE_SDV2HC)
{
sector = sector<<9;
}
/* Send CMD24 : Write single block command */
r1 = _send_command(CMD24, sector, 0);
if(r1 != 0x00)
{
return 1;
}
/* Card enable, Prepare to write */
_card_enable();
spi_read_write(DUMMY_BYTE);
spi_read_write(DUMMY_BYTE);
spi_read_write(DUMMY_BYTE);
/* Start data write token: 0xFE */
spi_read_write(0xFE);
/* Start single block write the data buffer */
for(i=0; i<MSD_BLOCKSIZE; i++)
{
spi_read_write(*buffer++);
}
/* 2Bytes dummy CRC */
spi_read_write(DUMMY_BYTE);
spi_read_write(DUMMY_BYTE);
/* MSD card accept the data */
r1 = spi_read_write(DUMMY_BYTE);
if((r1&0x1F) != 0x05)
{
_card_disable();
return 2;
}
/* Wait all the data programm finished */
retry = 0;
while(spi_read_write(DUMMY_BYTE) == 0x00)
{
/* Timeout return */
if(retry++ == 0x40000)
{
_card_disable();
return 3;
}
}
/* chip disable and dummy byte */
_card_disable();
spi_read_write(DUMMY_BYTE);
return 0;
}
/*******************************************************************************
* Function Name : MSD_WriteMultiBlock
* Description : None
* Input : - sector:
* - buffer:
* - NbrOfSector:
* Output : None
* Return : None
* Attention : None
*******************************************************************************/
int MSD_WriteMultiBlock(uint32_t sector, uint8_t *buffer, uint32_t NbrOfSector)
{
uint8_t r1;
uint16_t i;
uint32_t n;
uint32_t retry;
/* if ver = SD2.0 HC, sector need <<9 */
if(CardInfo.CardType != CARDTYPE_SDV2HC)
{
sector = sector<<9;
}
/* Send command ACMD23 berfore multi write if is not a MMC card */
if(CardInfo.CardType != CARDTYPE_MMC)
{
_send_command(ACMD23, NbrOfSector, 0x00);
}
/* Send CMD25 : Write nulti block command */
r1 = _send_command(CMD25, sector, 0);
if(r1 != 0x00)
{
return 1;
}
/* Card enable, Prepare to write */
_card_enable();
spi_read_write(DUMMY_BYTE);
spi_read_write(DUMMY_BYTE);
spi_read_write(DUMMY_BYTE);
for(n=0; n<NbrOfSector; n++)
{
/* Start multi block write token: 0xFC */
spi_read_write(0xFC);
for(i=0; i<MSD_BLOCKSIZE; i++)
{
spi_read_write(*buffer++);
}
/* 2Bytes dummy CRC */
spi_read_write(DUMMY_BYTE);
spi_read_write(DUMMY_BYTE);
/* MSD card accept the data */
r1 = spi_read_write(DUMMY_BYTE);
if((r1&0x1F) != 0x05)
{
_card_disable();
return 2;
}
/* Wait all the data programm finished */
retry = 0;
while(spi_read_write(DUMMY_BYTE) != 0xFF)
{
/* Timeout return */
if(retry++ == 0x40000)
{
_card_disable();
return 3;
}
}
}
/* Send end of transmit token: 0xFD */
r1 = spi_read_write(0xFD);
if(r1 == 0x00)
{
return 4;
}
/* Wait all the data programm finished */
retry = 0;
while(spi_read_write(DUMMY_BYTE) != 0xFF)
{
/* Timeout return */
if(retry++ == 0x40000)
{
_card_disable();
return 5;
}
}
/* chip disable and dummy byte */
_card_disable();
spi_read_write(DUMMY_BYTE);
return 0;
}
/*******************************************************************************
* Function Name : _send_command
* Description : None
* Input : - cmd:
* - arg:
* - crc:
* Output : None
* Return : R1 value, response from card
* Attention : None
*******************************************************************************/
int _send_command(uint8_t cmd, uint32_t arg, uint8_t crc)
{
uint8_t r1;
uint8_t retry;
/* Dummy byte and chip enable */
spi_read_write(DUMMY_BYTE);
_card_enable();
/* Command, argument and crc */
spi_read_write(cmd | 0x40);
spi_read_write(arg >> 24);
spi_read_write(arg >> 16);
spi_read_write(arg >> 8);
spi_read_write(arg);
spi_read_write(crc);
/* Wait response, quit till timeout */
for(retry=0; retry<200; retry++)
{
r1 = spi_read_write(DUMMY_BYTE);
if(r1 != 0xFF)
{
break;
}
}
/* Chip disable and dummy byte */
_card_disable();
spi_read_write(DUMMY_BYTE);
return r1;
}
/*******************************************************************************
* Function Name : _send_command_hold
* Description : None
* Input : - cmd:
* - arg:
* - crc:
* Output : None
* Return : R1 value, response from card
* Attention : None
*******************************************************************************/
int _send_command_hold(uint8_t cmd, uint32_t arg, uint8_t crc)
{
uint8_t r1;
uint8_t retry;
/* Dummy byte and chip enable */
spi_read_write(DUMMY_BYTE);
_card_enable();
/* Command, argument and crc */
spi_read_write(cmd | 0x40);
spi_read_write(arg >> 24);
spi_read_write(arg >> 16);
spi_read_write(arg >> 8);
spi_read_write(arg);
spi_read_write(crc);
/* Wait response, quit till timeout */
for(retry=0; retry<200; retry++)
{
r1 = spi_read_write(DUMMY_BYTE);
if(r1 != 0xFF)
{
break;
}
}
return r1;
}
下面就是FATFS文件系统的移植了。实现一下移植接口:
const Diskio_drvTypeDef SD_Driver =
{
SD_initialize,
SD_status,
SD_read,
#if _USE_WRITE == 1
SD_write,
#endif /* _USE_WRITE == 1 */
#if _USE_IOCTL == 1
SD_ioctl,
#endif /* _USE_IOCTL == 1 */
};
/**
* @brief Initializes a Drive
* @param lun : not used
* @retval DSTATUS: Operation status
*/
DSTATUS SD_initialize(BYTE lun)
{
if(MSD_Init() == 0)
{
MSD_GetCardInfo(&CardInfo);
return 0;
}else return STA_NOINIT;
}
/**
* @brief Gets Disk Status
* @param lun : not used
* @retval DSTATUS: Operation status
*/
DSTATUS SD_status(BYTE lun)
{
return 0;
}
/**
* @brief Reads Sector(s)
* @param lun : not used
* @param *buff: Data buffer to store read data
* @param sector: Sector address (LBA)
* @param count: Number of sectors to read (1..128)
* @retval DRESULT: Operation result
*/
DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
{
int res;
if( !count )
{
return RES_PARERR; /* count不能等于0,否则返回参数错误 */
}
if(count==1) /* 1个sector的读操作 */
{
res = MSD_ReadSingleBlock(sector ,buff );
}else /* 多个sector的读操作 */
{
res = MSD_ReadMultiBlock(sector , buff ,count);
}
if(res == 0)
{
return RES_OK;
}else
{
return RES_ERROR;
}
}
/**
* @brief Writes Sector(s)
* @param lun : not used
* @param *buff: Data to be written
* @param sector: Sector address (LBA)
* @param count: Number of sectors to write (1..128)
* @retval DRESULT: Operation result
*/
#if _USE_WRITE == 1
DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
{
int res;
if( !count )
{
return RES_PARERR; /* count不能等于0,否则返回参数错误 */
}
if(count==1) /* 1个sector的写操作 */
{
res = MSD_WriteSingleBlock(sector , (uint8_t *)(&buff[0]) );
}else /* 多个sector的写操作 */
{
res = MSD_WriteMultiBlock(sector , (uint8_t *)(&buff[0]) , count );
}
if(res == 0)
{
return RES_OK;
}else
{
return RES_ERROR;
}
}
#endif /* _USE_WRITE == 1 */
/**
* @brief I/O control operation
* @param lun : not used
* @param cmd: Control code
* @param *buff: Buffer to send/receive control data
* @retval DRESULT: Operation result
*/
#if _USE_IOCTL == 1
DRESULT SD_ioctl(BYTE lun, BYTE cmd, void *buff)
{
DRESULT res = RES_ERROR;
switch (cmd)
{
/* Make sure that no pending write process */
case CTRL_SYNC :
res = RES_OK;
break;
/* Get number of sectors on the disk (DWORD) */
case GET_SECTOR_COUNT :
*(DWORD*)buff = CardInfo.Capacity/CardInfo.BlockSize;
res = RES_OK;
break;
/* Get R/W sector size (WORD) */
case GET_SECTOR_SIZE :
*(WORD*)buff = CardInfo.BlockSize;
res = RES_OK;
break;
/* Get erase block size in unit of sector (DWORD) */
case GET_BLOCK_SIZE :
*(WORD*)buff = CardInfo.BlockSize;
res = RES_OK;
break;
default:
res = RES_PARERR;
}
return res;
}
#endif /* _USE_IOCTL == 1 */
下面是FATFS初始化:
int FATFS_Init(FATFS *fs,char * _path)
{
FRESULT res;
FATFS_LinkDriver(&SD_Driver,_path, 0);
res = f_mount(fs,_path,1);
printf("f_mount return =%d\r\n",res);
return res;
}
FATFS文件系统配置在ffconf.h文件内,按照自己需求配置,需要修改的部分挺少的。
下面就是main主函数测试移植效果了,主要是测试扫描TF卡内所有文件并在串口打印出来。
FATFS fs; /* Work area (file system object) for logical drive */
char path[256]="0:/";
uint8_t textFileBuffer[] = "Thank you for using Development Board ^_^ \r\n";
FRESULT scan_files (char* path)
{
FRESULT res;
FILINFO fno;
DIR dir;
int i;
char *fn;
res = f_opendir(&dir, path);
if (res == FR_OK)
{
i = strlen(path);
for (;;)
{
res = f_readdir(&dir, &fno);
if (res != FR_OK || fno.fname[0] == 0) break;
if (fno.fname[0] == '.') continue;
#if FF_USE_LFN
fn = *fno.fname ? fno.fname : fno.altname; //长文件名
#else
fn = fno.fname; //短文件名
#endif
if (fno.fattrib & AM_DIR) //文件夹
{
sprintf(&path[i], "%s/", fn);
res = scan_files(path);
if (res != FR_OK) break;
path[i] = 0;
} else //文件
{
printf("%s%s \r\n", path, fn);
}
}
f_closedir(&dir);
}else printf("f_opendir %s error %d!\r\n",path,res);
return res;
}
/*********************************************************************
* @fn main
*
* @brief Main program.
*
* @return none
*/
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
Delay_Init();
GPIO_INIT();
USART_Printf_Init(115200);
printf("SystemClk:%d\r\n", SystemCoreClock);
printf("This is TF card example.\r\n");
if(FATFS_Init(&fs,path) == 0)
{
printf("FATFS Init OK.\r\n");
scan_files(path);
}else {
printf("FATFS Init Fail!!!\r\n");
}
while(1)
{
GPIO_SetBits(GPIOA,GPIO_Pin_0 | GPIO_Pin_1);
printf("led on.\r\n");
Delay_Ms(500);
GPIO_ResetBits(GPIOA,GPIO_Pin_0 | GPIO_Pin_1);
printf("led off.\r\n");
Delay_Ms(500);
}
}
开发板接线如下:
串口打印数据:
更多回帖