關鍵代碼:
diskio.c
DSTATUS disk_status (
BYTE pdrv /* 磁盤的驅動器編号 */
)
{
DSTATUS stat = STA_NOINIT;
switch (pdrv) {
/* 解析磁盤驅動器編号 */
case DEV_SDIO_SD :
stat &= ~STA_NOINIT;
return stat;
case DEV_SPI_FLASH :
return stat;
}
return STA_NOINIT;
}
/*-----------------------------------*/
/* 初始化磁盤 */
/*-----------------------------------*/
DSTATUS disk_initialize (
BYTE pdrv /* 磁盤的驅動器編号 */
)
{
DSTATUS stat = STA_NOINIT;
switch (pdrv) {
case DEV_SDIO_SD :
if( SD_Init() == SD_OK )
{
stat &= ~STA_NOINIT;
return stat;
}
case DEV_SPI_FLASH :
return stat;
}
return STA_NOINIT;
}
/*------------------------------------*/
/* 讀磁盤扇區 */
/*------------------------------------*/
DRESULT disk_read (
BYTE pdrv, /* 磁盤的驅動器編号 */
BYTE *buff, /* 存放讀取資料的緩存區 */
DWORD sector, /* LBA 模式下的起始扇區 */
UINT count /* 要讀取的扇區數 */
)
{
DRESULT res = RES_PARERR;
SD_Error SD_status;
switch (pdrv) {
case DEV_SDIO_SD :
/* 從位址 0 開始讀取多個位元組資料 */
SD_status = SD_ReadMultiBlocks(buff, sector*SDCardInfo.CardBlockSize,
SDCardInfo.CardBlockSize, count);
if(SD_status != SD_OK)
return res = RES_ERROR;
/* 判斷是否傳輸已完畢 */
SD_status = SD_WaitReadOperation();
if(SD_status != SD_OK)
return res = RES_ERROR;
while(SD_GetStatus() != SD_TRANSFER_OK);
res = RES_OK;
return res;
case DEV_SPI_FLASH :
res = RES_OK;
return res;
}
return RES_PARERR;
}
/*------------------------------------------*/
/* 寫入磁盤扇區 */
/*------------------------------------------*/
#if FF_FS_READONLY == 0
DRESULT disk_write (
BYTE pdrv, /* 磁盤的驅動器編号 */
const BYTE *buff, /* 要寫入的資料 */
DWORD sector, /* LBA 模式下的起始扇區 */
UINT count /* 要寫入的扇區數 */
)
{
DRESULT res = RES_PARERR;
SD_Error SD_status;
if (!count) {
return RES_PARERR; /* 指明參數錯誤 */
}
switch (pdrv) {
case DEV_SDIO_SD :
/* 向位址 0 寫入多個位元組 */
SD_status = SD_WriteMultiBlocks((uint8_t *)buff, sector * \
SDCardInfo.CardBlockSize,SDCardInfo.CardBlockSize, count);
/* 判斷是否傳輸已經結束 */
if(SD_status != SD_OK)
return res = RES_ERROR;
SD_status = SD_WaitWriteOperation();
if(SD_status != SD_OK)
return res = RES_ERROR;
while(SD_GetStatus() != SD_TRANSFER_OK) ;
res = RES_OK;
return res;
case DEV_SPI_FLASH :
res = RES_OK;
return res;
}
return RES_PARERR;
}
#endif
/*------------------------------------------*/
/* 磁盤控制的其他功能 */
/*------------------------------------------*/
DRESULT disk_ioctl (
BYTE pdrv, /* 磁盤驅動器的編号 (0..) */
BYTE cmd, /* 控制碼 */
void *buff /* 存放收發控制資料的緩存 */
)
{
DRESULT res = RES_PARERR;
switch (pdrv)
{
case DEV_SDIO_SD :
switch (cmd)
{
case CTRL_SYNC:
res = RES_OK;
break;
/* 擷取扇區數量 */
case GET_SECTOR_COUNT:
*(DWORD *)buff = SDCardInfo.CardCapacity/SDCardInfo.CardBlockSize;
res = RES_OK;
break;
/* 擷取扇區大小 */
case GET_SECTOR_SIZE :
*(WORD * )buff = SDCardInfo.CardBlockSize;
res = RES_OK;
break;
/* 擷取塊的大小 */
case GET_BLOCK_SIZE :
*(DWORD * )buff = 1;
res = RES_OK;
break;
default:
res = RES_PARERR;
break;
}
break;
case DEV_SPI_FLASH :
/* 在這裡處理 SPIFlash 操作相關的指令 */
break;
}
return res;
}
__weak uint32_t get_fattime(void)
{
uint16_t year;
uint8_t month,day,hour,minute,second;
RTC_TimeTypeDef RTC_TimeStructure;
RTC_DateTypeDef RTC_DateStructure;
/* 擷取日期和時間 */
RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure);
RTC_GetDate(RTC_Format_BIN, &RTC_DateStructure);
/* 日期和時間指派 */
year = RTC_DateStructure.RTC_Year + 2000;
month = RTC_DateStructure.RTC_Month;
day = RTC_DateStructure.RTC_Date;
hour = RTC_TimeStructure.RTC_Hours;
minute = RTC_TimeStructure.RTC_Minutes;
second = RTC_TimeStructure.RTC_Seconds;
/* 傳回目前時間戳 */
return ((uint32_t)(year - 1980) << 25) /* 年 */
| ((uint32_t)month << 21) /* 月 */
| ((uint32_t)day << 16) /* 日 */
| ((uint32_t)hour << 11) /* 小時 */
| ((uint32_t)minute << 5) /* 分鐘 */
| ((uint32_t)second >> 1); /* 秒 */
}
bsp_rtc.c
/**
* @brief RTC 配置:選擇 RTC 時鐘源,設定 RTC_CLK 的分頻系數
* @param 0: 時鐘源選擇 LSI , 1: 時鐘源選擇 LSE
* @retval None
*/
void RTC_CLK_Config(uint8_t RTC_Clock_Type)
{
RTC_InitTypeDef RTC_InitStructure;
/* 使能 PWR 時鐘 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
/* 使能 RTC 、 RTC 備份寄存器和備份 SRAM 的通路 */
PWR_BackupAccessCmd(ENABLE);
if(RTC_Clock_Type == 0)
{
/* 使能 LSI */
RCC_LSICmd(ENABLE);
/* 等待 LSI 穩定 */
while(RCC_GetFlagStatus(RCC_FLAG_LSIRDY) == RESET);
/* 選擇 LSI 作為 RTC 的時鐘源 */
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSI);
}
else if(RTC_Clock_Type == 1)
{
/* 使能 LSE */
RCC_LSEConfig(RCC_LSE_ON);
/* 等待 LSE 穩定 */
while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);
/* 選擇 LSE 作為 RTC 的時鐘源 */
RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);
}
RCC_RTCCLKCmd(ENABLE); // 使能 RTC 時鐘
RTC_WaitForSynchro(); // 等待 RTC 的 APB 寄存器同步
/* 設定異步預分頻器的值 */
RTC_InitStructure.RTC_AsynchPrediv = ASYNCH_PREDIV;
/* 設定同步預分頻器的值 */
RTC_InitStructure.RTC_SynchPrediv = SYNCH_PREDIV;
RTC_InitStructure.RTC_HourFormat = RTC_HourFormat_24;
/* 用 RTC_InitStructure 的内容初始化 RTC 寄存器 */
if(RTC_Init(&RTC_InitStructure) == ERROR)
{
printf("\n\r RTC 時鐘初始化失敗 \r\n");
}
else
{
printf("\n\r RTC 時鐘初始化成功 \r\n");
}
}
/**
* @brief 設定日期和時間
* @param None
* @retval None
*/
void RTC_Set_DateTime(void)
{
RTC_TimeTypeDef RTC_TimeStructure;
RTC_DateTypeDef RTC_DateStructure;
/* 初始化日期 */
RTC_DateStructure.RTC_WeekDay = WEEKDAY;
RTC_DateStructure.RTC_Date = DATE;
RTC_DateStructure.RTC_Month = MONTH;
RTC_DateStructure.RTC_Year = YEAR;
RTC_SetDate(RTC_Format_BINorBCD, &RTC_DateStructure);
/* 初始化時間 */
RTC_TimeStructure.RTC_H12 = RTC_H12_AMorPM;
RTC_TimeStructure.RTC_Hours = HOURS;
RTC_TimeStructure.RTC_Minutes = MINUTES;
RTC_TimeStructure.RTC_Seconds = SECONDS;
RTC_SetTime(RTC_Format_BINorBCD, &RTC_TimeStructure);
/* 往備份域寄存器 0 寫入一個值 */
RTC_WriteBackupRegister(RTC_BKP_DR0, 0x5F5F);
}
/**
* @brief 擷取目前日期和時間
* @param None
* @retval None
*/
static void RTC_Get_DateTime(void)
{
RTC_GetTime(RTC_Format_BIN, &myRTC_TimeStructure);
RTC_GetDate(RTC_Format_BIN, &myRTC_DateStructure);
}
/* 以下函數用于擷取目前的“年 / 月 / 日 / 星期 / 小時 / 分鐘 / 秒”值 */
uint8_t RTC_Get_Year(void)
{
RTC_Get_DateTime();
return myRTC_DateStructure.RTC_Year;
}
uint8_t RTC_Get_Month(void)
{
RTC_Get_DateTime();
return myRTC_DateStructure.RTC_Month;
}
uint8_t RTC_Get_Date(void)
{
RTC_Get_DateTime();
return myRTC_DateStructure.RTC_Date;
}
uint8_t RTC_Get_WeekDay(void)
{
RTC_Get_DateTime();
return myRTC_DateStructure.RTC_WeekDay;
}
uint8_t RTC_Get_Hour(void)
{
RTC_Get_DateTime();
return myRTC_TimeStructure.RTC_Hours;
}
uint8_t RTC_Get_Minute(void)
{
RTC_Get_DateTime();
return myRTC_TimeStructure.RTC_Minutes;
}
uint8_t RTC_Get_Second(void)
{
RTC_Get_DateTime();
return myRTC_TimeStructure.RTC_Seconds;
}
main.c
/**
* @brief 判斷目前時間是否是整點時間
* @param None
* @retval None
*/
bool Check_Certain_Hour(void)
{
uint8_t hour,minute,second;
RTC_TimeTypeDef RTC_TimeStructure;
RTC_GetTime(RTC_Format_BIN, &RTC_TimeStructure);
hour = RTC_TimeStructure.RTC_Hours;
minute = RTC_TimeStructure.RTC_Minutes;
second = RTC_TimeStructure.RTC_Seconds;
if(minute == 0 && second == 0)
{
if(hour != currentHour)
{
currentHour = hour;
return true;
}
}
return false;
}
/**
* @brief 存儲時間戳與環境參數函數
* @param None
* @retval None
*/
void Save_Data(void)
{
uint16_t year;
uint8_t month,day,hour,minute,second;
/* 在 SD 存儲卡挂載檔案系統 */
res = f_mount(&sysFs,"3:",1);
if(res == FR_NO_FILESYSTEM)
{
printf(">>> No file system, formatting...\r\n");
res = f_mkfs("3:", FM_FAT32, Buff, sizeof(Buff));
if(res == FR_OK)
{
printf(">>> SD memory card is successfully formatted.\r\n");
res = f_mount(NULL,"3:",1); // 格式化後,先取消挂載
res = f_mount(&sysFs,"3:",1); // 重新挂載
}
else
{
printf(">>> Format failed. (%d)\r\n",res);
while(1);
}
}
else if(res != FR_OK)
{
printf(">>> Mount FS failed. (%d)\r\n",res); // 給出錯誤提示和錯誤類型
while(1);
}
else if(res == FR_OK)
{
printf(">>> Mount FS success, you can operate read write test.\r\n");
}
/* 采集環境參數 */
bh1750Light = BH1750_Measure(); // 讀取 BH1750 的光照強度值
DHT11_Read_Data(&temperature,&humidity); // 讀取 DHT11 的溫濕度值
/* 獲得目前日期和時間 */
year = RTC_Get_Year() + 2000; // “年份”值需要加 2000
month = RTC_Get_Month();
day = RTC_Get_Date();
hour = RTC_Get_Hour();
minute = RTC_Get_Minute();
/* 将各資料組合成字元串 */
sprintf(strFinal,"%d;%0.2d-%0.2d;%0.2d:00;%d;%d;%d\r\n",year,month,day,\
hour,temperature,humidity,bh1750Light);
/* 打開檔案,如果檔案不存在則建立它 */
res = f_open(&fil, "sd:/Data.txt", FA_OPEN_ALWAYS | FA_WRITE );
/* 檔案讀寫指針定位到最後 */
f_lseek(&fil, f_size(&fil));
if (res == FR_OK)
{
printf(">>> Open/Create file(.txt) success, writting...\r\n");
/* 将指定存儲區内容寫入檔案内 */
res=f_write(&fil,strFinal,strlen(strFinal),&fnum);
if(res==FR_OK)
{
printf(">>> 檔案寫入成功,共寫入位元組資料: %d\r\n",fnum);
printf(">>> 此次寫入的資料是 :\r\n%s\r\n",strFinal);
}
else
{
printf(">>> 檔案寫入失敗 :(%d)\n",res);
}
/* 不再讀寫,關閉檔案 */
f_close(&fil);
}
else
{
printf(">>> Open/Create file(.txt) failed.\r\n");
}
}
int main(void)
{
delay_init(168); // 延時函數初始化
LED_Init(); //LED 端口初始化
Key_Init(); // 按鍵初始化
EXTIx_Init(); // 外部中斷初始化
USART1_Init(115200); //USART1 初始化
IIC_Init(); //IIC 總線初始化
BH1750_Init(); //BH1750 初始化
RTC_CLK_Config(1); //RTC 配置,時鐘源選擇 LSE
SDIO_NVIC_Configuration(); //SDIO 中斷和 DMA 中斷 NVIC 初始化
while(DHT11_Init()) //DHT11 初始化
{
printf("DHT11 Init Error!\r\n");
delay_ms(500);
}
printf("DHT11 Init Success!\r\n");
/* 若備份區域讀取的值不符 */
if (RTC_ReadBackupRegister(RTC_BKP_DR0) != 0x5F5F)
{
RTC_Set_DateTime(); // 重新設定時間和日期
}
else
{
printf("\r\n 不需要重新配置 RTC....\r\n");
/* 使能 PWR 時鐘 */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);
/* PWR_CR:DBF 置 1 ,使能 RTC 、 RTC 備份寄存器和備份 SRAM 的通路 */
PWR_BackupAccessCmd(ENABLE);
/* 等待 RTC APB 寄存器同步 */
RTC_WaitForSynchro();
}
currentHour = RTC_Get_Hour(); // 首次擷取目前小時值
printf("System Started!\r\n");
while(1)
{
/* 左鍵按下, SDIO 基本讀寫 SD 存儲卡測試 */
if(keyValue == KEY_L_PRESS)
{
SD_Test();
keyValue = 0;
}
/* 如果目前為整點 */
if(Check_Certain_Hour())
{
printf(" 過了一個小時 \r\n");
Save_Data();// 存儲時間戳和環境參數至 SD 存儲卡
}
}
}