W25Q64初始化
W25Q64用来存储用户设置的闹钟值,开机的时候自动读取闹钟记录。
SPI初始化
使用CubeMx自动配置。
#define SPI1CS_ON HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_RESET)
#define SPI1CS_OFF HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET)
static volatile bool gSPI1TxCplt = 0;
static volatile bool gSPI1RxCplt = 0;
static void SPI1WaitTxCplt(void)
{
while(gSPI1TxCplt == 0);
gSPI1TxCplt = 0;
}
static void SPI1WaitRxCplt(void)
{
while(gSPI1RxCplt == 0);
gSPI1RxCplt = 0;
}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
if(SPI1 == hspi->Instance)
{
gSPI1TxCplt = 1;
}
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
if(SPI1 == hspi->Instance)
{
gSPI1RxCplt = 1;
}
}
static int SpiWrite(unsigned char *wbuf, unsigned int length){
HAL_StatusTypeDef status = HAL_SPI_Transmit_DMA(&hspi1, wbuf, length);
if(HAL_OK != status) return -1;
SPI1WaitTxCplt();
return length;
}
static int SpiRead(unsigned char *rbuf, unsigned int length){
HAL_StatusTypeDef status = HAL_SPI_Receive_DMA(&hspi1, rbuf, length);
if(HAL_OK != status) return -1;
SPI1WaitRxCplt();
return length;
}
W25Q初始化
使用SPI读取W25Q的ID验证通信的正确性
static unsigned int ReadID(void)
{
unsigned char cmd = 0x9F; // W25X_JedecDeviceID
unsigned char id[3] = {0};
SPI1CS_ON;
SpiWrite(&cmd,1);
SpiRead(id, 3);
SPI1CS_OFF;
unsigned int ID = (id[0] << 16) | (id[1] << 8) | (id[2]);
return ID;
}
void W25QInit(void){
int ID = ReadID();
if(ID != 0xEF4017)
return;
printf("ID is %d",ID);
}
int W25QPageWrite(uint32_t addr,unsigned char *buf,unsigned int length){
unsigned char data[4];
data[0] = 0x02;
data[1] = (addr &0xFF0000)>>16;
data[2] = (addr &0x00FF00)>>8;
data[3] = (addr &0x0000FF);
W25QWriteEnable();
SPI1CS_ON;
int ret = SpiWrite(data,4);
if(ret < 0){
SPI1CS_OFF;
}
ret = SpiWrite(buf,length);
if(ret < 0){
SPI1CS_OFF;
}
SPI1CS_OFF;
WaitWrite();
return (int)length;
}
int W25QRead(uint32_t addr,unsigned char *buf,unsigned int length){
unsigned char data[4];
data[0] = 0x03;
data[1] = (addr &0xFF0000)>>16;
data[2] = (addr &0x00FF00)>>8;
data[3] = (addr &0x0000FF);
SPI1CS_ON;
int ret = SpiWrite(data,4);
if(ret < 0){
SPI1CS_OFF;
}
ret = SpiRead(buf,length);
if(ret != length){
SPI1CS_OFF;
}
SPI1CS_OFF;
return (int)length;
}
void W25QEraseSector(uint32_t addr){
unsigned char data[4];
data[0] = 0x20;
data[1] = (addr &0xFF0000)>>16;
data[2] = (addr &0x00FF00)>>8;
data[3] = (addr &0x0000FF);
W25QWriteEnable();
SPI1CS_ON;
int ret = SpiWrite(data,4);
if(ret < 0){
SPI1CS_OFF;
}
SPI1CS_OFF;
WaitWrite();
}
AlARM模块初始化
设置闹钟界面的信息
/*
* 描述:设置闹钟界面的页面加载函数
* 参数:无
* 返回值:无
*/
static void AlarmLoader(uint8_t num){
if(num < ALARM_COUNT-1)
{
alarm_s alarm2;
if(setting.now == SETTING_NOW_NONE || num != Main_Menu.selected)
Alarmget(num, &alarm2);
else
memcpy(&alarm2, &alarm, sizeof(alarm_s));
AddAlarmBasic(num, &alarm2);
}
addExitOption();
}
/*
* 描述:设置闹钟界面的确认函数
* 参数:无
* 返回值:无
*/
static void AlarmSelect(){
bool isExiting = exitSelected();
GetMenupre(&alarmMenus);
DoAction(isExiting);
}
/*
* 描述:设置闹钟界面的按钮函数等菜单基本信息
* 参数:无
* 返回值:无
*/
void AlarmMenu(void){
SetButtonFunc(menuUp, (bool (*)(void))AlarmSelect, menuDown);
SetMenuFunc(preOption, AlarmSelect, nextOption,AlarmLoader);
SetMenuInfo(ALARM_COUNT,MENU_TYPE_STR,PSTR(STR_ALARMMENU));
SetMenuPre(&alarmMenus,AlarmMenu);
AnimotionInto(NULL);
}
AlARM模块绘制Draw函数
alarm模块是STR菜单界面,更改界面的值通过状态机的方式逐个改变每个数据的值然后通过确认赋值,改变完所有数据值后保存到外部Flash中。
static void AlarmUp(void){
setting.val++;
if(setting.val > getMaxValForSetting())
setting.val = 0;
if(setting.now == SETTING_NOW_HOUR && appConfig.timeMode == TIMEMODE_12HR && setting.val == 0)
setting.val = 1;
}
static void AlarmDown(void){
setting.val--;
if(setting.now == SETTING_NOW_HOUR && appConfig.timeMode == TIMEMODE_12HR && setting.val == 0)
setting.val = 12;
uint8_t max = getMaxValForSetting();
if(setting.val > max)
setting.val = max;
}
static void AlarmDraw(void){
uint8_t w = 5;
uint8_t x;
char buff[4];
buff[1] = 0x00;
switch(setting.now)
{
case SETTING_NOW_EN:
x = 6;
w = 8;
buff[0] = setting.val ? ALARM_SET_CHAR : ALARM_UNSET_CHAR;
break;
case SETTING_NOW_HOUR:
x = 22;
w = 16;
break;
case SETTING_NOW_MIN:
x = 46;
w = 16;
break;
case SETTING_NOW_AMPM:
x = 62;
w = 8;
buff[0] = setting.val ? CHAR_PM : CHAR_AM;
break;
case SETTING_NOW_DAY_MON:
case SETTING_NOW_DAY_TUE:
case SETTING_NOW_DAY_WED:
case SETTING_NOW_DAY_THUR:
case SETTING_NOW_DAY_FRI:
case SETTING_NOW_DAY_SAT:
case SETTING_NOW_DAY_SUN:
{
uint8_t dow = setting.now - SETTING_NOW_DAY_MON;
x = 70 + (8 * dow);
w = 8;
buff[0] = setting.val ? dowChars[dow] : '-';
}
break;
default:
return;
}
uint8_t y = 8 + ((Main_Menu.selected * 8) - (Main_Menu.scroll * 8));
OledClearArea(x, y, w);
if(setting.now == SETTING_NOW_HOUR || setting.now == SETTING_NOW_MIN)
sprintf(buff, PSTR("%02hhu"), setting.val);
OledShowStr(x,y,buff,OLED_8X8);
}
static void SelectedAlarm()
{
static uint8_t dayBit;
static bool wasPM;
SetMenuFunc(AlarmUp, AlarmSelect, AlarmDown, AlarmLoader);
Main_Menu.func.draw = AlarmDraw;
switch(setting.now)
{
case SETTING_NOW_NONE:
Alarmget(Main_Menu.selected, &alarm);
setting.now = SETTING_NOW_EN;
setting.val = alarm.enabled;
break;
case SETTING_NOW_EN:
alarm.enabled = setting.val;
setting.now = SETTING_NOW_HOUR;
time_s time;
time.hour = alarm.hour;
time.ampm = CHAR_24;
TimeSetMode(&time, appConfig.timeMode);
setting.val = time.hour;
wasPM = (alarm.hour >= 12);
break;
case SETTING_NOW_HOUR:
{
uint8_t hour = setting.val;
uint8_t max = appConfig.timeMode == TIMEMODE_12HR ? 12 : 23;
if(hour > max)
hour = max;
if(appConfig.timeMode == TIMEMODE_12HR)
{
time_s time;
time.hour = hour;
time.ampm = wasPM ? CHAR_PM : CHAR_AM;
TimeSetMode(&time, TIMEMODE_24HR);
hour = time.hour;
}
alarm.hour = hour;
setting.now = SETTING_NOW_MIN;
setting.val = alarm.min;
}
break;
case SETTING_NOW_MIN:
alarm.min = setting.val;
if(appConfig.timeMode == TIMEMODE_12HR)
{
setting.now = SETTING_NOW_AMPM;
setting.val = wasPM;
}
else
{
setting.now = SETTING_NOW_DAY_MON;
setting.val = alarm.mon;
}
dayBit = 0;
break;
case SETTING_NOW_AMPM:
{
time_s time;
time.hour = alarm.hour;
time.ampm = setting.val ? CHAR_PM : CHAR_AM;
TimeSetMode(&time, TIMEMODE_24HR);
alarm.hour = time.hour;
setting.now = SETTING_NOW_DAY_MON;
setting.val = alarm.mon;
}
break;
case SETTING_NOW_DAY_MON:
case SETTING_NOW_DAY_TUE:
case SETTING_NOW_DAY_WED:
case SETTING_NOW_DAY_THUR:
case SETTING_NOW_DAY_FRI:
case SETTING_NOW_DAY_SAT:
case SETTING_NOW_DAY_SUN:
if(setting.val)
alarm.days |= _BV(dayBit);
else
alarm.days &= ~_BV(dayBit);
dayBit++;
setting.val = (bool)(alarm.days & _BV(dayBit));
if(setting.now != SETTING_NOW_DAY_SUN)
setting.now++;
else
{
AlarmSave(Main_Menu.selected, &alarm);
setting.now = SETTING_NOW_NONE;
SetMenuFunc(preOption, AlarmSelect, nextOption,AlarmLoader);
Main_Menu.func.draw = NULL;
}
break;
default:
break;
}
}
AlARM的更新函数
如果当前时间是闹钟设置的时间,则进行相应。改变当前页面绘制函数称为闹钟响后的绘制函数。
void AlarmUpdate(void){
bool wasGoingOff = alarmSetOff;
bool alarmNow = goingOff();
if(alarmSetOff)
{
if(alarmNow)
{
if(wasGoingOff != alarmSetOff)
{
oldDrawFunc = SetDrawFunc(AlarmDraw);
oldBtn1Func = SetButtonSignedFunc(BTN1, NULL);
oldBtn2Func = SetButtonSignedFunc(BTN2, (bool (*)(void))AlarmStop);
oldBtn3Func = SetButtonSignedFunc(BTN3, NULL);
BuzzerPlay(tuneAlarm,VOL_ALARM,PRIO_ALARM);
}
if(!LedFlashing())
{
static led_t ledFlash = LEDGREEN;
ledFlash = (ledFlash == LEDGREEN) ? LEDRED : LEDGREEN;
LedFlash(ledFlash, 150);
}
}
else
AlarmStop();
}
}
static void AlarmDraw(void){
if((uint8_t)xTaskGetTickCount() < 128)
OledShowImage(16, 16, 32, 32, &MAIN_MUNE[3][0], 0);
// Draw time
char buff[8];
sprintf(buff, PSTR(TIME_FORMAT_SMALL), timeDate.time.hour, timeDate.time.mins, timeDate.time.ampm);
OledShowStr(60, 20,buff,OLED_8X8);
// Draw day
strcpy(buff, days[timeDate.date.day]);
OledShowStr(68,36,buff,OLED_8X8);
}
AlARM中最重要的函数
每次时间或者闹钟响后,更新最近一个闹钟。通过计算本周开始到当前闹钟时间总共过去多少分钟,最少的就是最近一个闹钟。
void GetNextAlarm(void){
uint8_t next = NOALARM;
uint32_t nextTime = (uint32_t)UINT_MAX;
// Make sure time is in 24 hour mode
time_s timeNow;
timeNow.hour = timeDate.time.hour;
timeNow.ampm = timeDate.time.ampm;
TimeSetMode(&timeNow, TIMEMODE_24HR);
// Now in minutes from start of week
uint32_t now = toMinutes(timeNow.hour, timeDate.time.mins + 1, timeDate.date.day);
// Loop through alarms
for(uint8_t i = 0;i <ALARM_COUNT; i++)
{
// Get alarm data
alarm_s alarm;
Alarmget(i, &alarm);
// Not enabled
if(!alarm.enabled)
continue;
// Loop through days
for(uint8_t d = 0;d <7; d++)
{
// Day not enabled
if(!AlarmDayEnabled(alarm.days, d))
continue;
// Alarm time in minutes from start of week
uint32_t alarmTime = toMinutes(alarm.hour, alarm.min, d);
// Minutes to alarm
int timeTo = alarmTime - now;
// Negative result, must mean alarm time is earlier in the week than now, add a weeks time
if(timeTo < 0)
timeTo += ((6*1440) + (23*60) + 59); // 10079
// Is minutes to alarm less than the last minutes to alarm?
if((uint32_t)timeTo < nextTime)
{
// This is our next alarm
nextTime = timeTo;
next = i;
}
}
}
// Set next alarm
nextAlarm = next;
}