prevent double-click

本文探讨了在不同版本的Rails框架中禁用按钮的方法,包括使用:disable_with选项及JavaScript解决方案,并介绍了如何防止链接被重复点击,特别是针对不支持CSS 'pointer-events'属性的Internet Explorer浏览器。
First we talk about the button.
Button or Submit
Solution 1: disable_with => "Begin Service"
Rails can use :disable_with => "Begin Service".
Example:
= button_tag "Begin Service", root_path, :disable_with => "Begin Service" 

This will not work when use RailsVersion Under(maybe 3.2) for image_submit_button.
So we can fix it like this:
Solution 2:
$("#an_image_submit").attr("disabled", true)

Link Add class = 'nodouble' to a link
Solution 1:
$(".nodouble").live "click", -> 
$(this).click -> return false

Solution 2:
$("body").on "click", ".nodouble", (e) -> 
$(this).attr "onclick", ""
$(this).on "click", (e) -> false

Solution 3: For Ajax query: CSS:
#'pointer-events: none' will not work in IE, So this solution is not suitable for IE. This solution's benifit is that it will change the CSS of the clicked link.
a.disabled { opacity: 0.5; pointer-events: none; cursor: default } 

$(".nodouble").livequery "click", (e) -> 
e.preventDefault()
a = $(this) a.addClass('disabled')

Because Solution 3 can not work with IE, So we should do more work like this: Solutions 4
$(".nodouble").livequery "click", (e) -> 
e.preventDefault()
a = $(this) return false if a.data("clicked") a.data("clicked", true)
a.addClass('disabled')
#include "main.h" #include "adc.h" #include "dma.h" #include "i2c.h" #include "rtc.h" #include "spi.h" #include "tim.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include "string.h" #include "oled.h" #include "stdio.h" #include "norflash.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ typedef enum { STATE_MAIN, // 主界面 STATE_MODE_SELECT, // 模式选择 STATE_TIMER_LIST, // 定时列表 STATE_TIMER_EDIT, // 定时编辑 STATE_TIME_EDIT } SystemState; //RTC时间设置 typedef enum { editTimeModeSetTime, editTimeConfirm, editTimeCancel } editTimeMode; typedef enum { SET_NONE, SET_YEAR, SET_MONTH, SET_DAY, SET_HOUR, SET_MINUTE } editTimeType; enum { EDIT_TIME,//修改时间 EDIT_TEMP,//修改温度 EDIT_SWITCH,//开关 EDIT_CONFIRM,//确认 EDIT_CANCEL,//取消 EDIT_CNT }; enum { EDIT_HOUR,//修改小时 EDIT_MINUTE,//修改分钟 EDIT_TARGET_TEMP,//修改目标温度 EDIT_STATE_SWITCH,//修改开关 EDIT_NONE//无操作 }; uint8_t uart1_rx[2]; uint8_t uart2_rx[256]; uint8_t uart3_rx[256]; /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ #define MAX_TEMP 60 #define MIN_TEMP 30 /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ // 系统状态 static int edit_field = 0; uint8_t warn_flag = 0; uint8_t total_power = 1; SystemState current_state = STATE_MAIN; //当前界面 uint8_t selected_index = 0; uint8_t target_temp = 50; uint8_t current_temp = 25; WorkMode current_mode = MODE_AUTO;//工作模式 PowerState current_power_state = POWER_OFF;//加热状态 short temperature;//温度 float temperature_f;//温度 float TDS_value = 0.0;//水质 waterLevel_t waterlevel = NOMAL_WATER; // 定时任务 TimerTask timers[3] = { {8, 0, 50, 0}, {12, 0, 45, 0}, {18, 0, 55, 0} }; //编辑时间 uint8_t edit_field_state = 0; TimerTask preTimer; extern unsigned char esp8266_buf[1024]; extern unsigned short esp8266_cnt; extern unsigned short esp8266_cntPre; struct tm nowTime; struct tm setTime; const char *topics[] = {"/sys/ifimVLfCKMh/stm32/thing/service/property/set"}; unsigned char *dataPtr = NULL; unsigned char *cmd = "AT+RST\r\n"; int connectServerFlag = 0;//服务器连接成功 int connectMqttFlag = 0;//订阅成功 int connectFlag = 0;//网络连接成功 uint32_t ADC_VALUE = 0; float ADC_ConvertedValueLocal; float kValue = 0.976; GPIO_PinState low_water, high_water; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); /* USER CODE BEGIN PFP */ #ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif PUTCHAR_PROTOTYPE { HAL_UART_Transmit(&huart1, (uint8_t * ) & ch, 1, HAL_MAX_DELAY); return ch; } void save_timers_to_flash(); /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /** * @brief 判断年份是否是闰年 * @note 月份天数表: * 月份 1 2 3 4 5 6 7 8 9 10 11 12 * 闰年 31 29 31 30 31 30 31 31 30 31 30 31 * 非闰年 31 28 31 30 31 30 31 31 30 31 30 31 * @param year : 年份 * @retval 0, 非闰年; 1, 是闰年; */ static uint8_t rtc_is_leap_year(uint16_t year) { /* 闰年规则: 四年闰百年不闰,四百年又闰 */ if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) { return 1; } else { return 0; } } __weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { /* Prevent unused argument(s) compilation warning */ if (htim == &htim5) { KEY_Scan(); // 每10ms扫描一次按键 } /* NOTE : This function should not be modified, when the callback is needed, the HAL_TIM_PeriodElapsedCallback could be implemented in the user file */ } void Display_MainScreen(void) { OLED_NewFrame(); char buf[20]; sprintf(buf, "%02d-%02d-%02d %02d:%02d:%02d", nowTime.tm_year + 1900, nowTime.tm_mon + 1, nowTime.tm_mday, nowTime.tm_hour, nowTime.tm_min, nowTime.tm_sec); OLED_PrintString(0, 0, buf, &font12x12, OLED_COLOR_NORMAL); sprintf(buf, "当前温度:%d.%d℃", temperature / 10, temperature % 10); OLED_PrintString(0, 12, buf, &font12x12, OLED_COLOR_NORMAL); sprintf(buf, "目标温度:%2d℃", target_temp); OLED_PrintString(0, 24, buf, &font12x12, OLED_COLOR_NORMAL); sprintf(buf, "水位:"); OLED_PrintString(0, 36, buf, &font12x12, OLED_COLOR_NORMAL); if(waterlevel == NOMAL_WATER){ OLED_PrintString(30, 36, "正常", &font12x12, OLED_COLOR_NORMAL); }else if(waterlevel == LOW_WATER){ OLED_PrintString(30, 36, "过低", &font12x12, OLED_COLOR_NORMAL); }else if(waterlevel == HIGH_WATER){ OLED_PrintString(30, 36, "过高", &font12x12, OLED_COLOR_NORMAL); } /* 自动模式 */ sprintf(buf, "水质:%.2f", TDS_value); OLED_PrintString(60, 36, buf, &font12x12, OLED_COLOR_NORMAL); // 显示当前模式 const char *mode_str[] = {"自动", "节能", "手动", "定时"}; sprintf(buf, "模式:%s", mode_str[current_mode]); OLED_PrintString(0, 48, buf, &font12x12, OLED_COLOR_NORMAL); if (current_power_state == POWER_ON) { OLED_PrintString(72, 48, "加热", &font12x12, OLED_COLOR_NORMAL); } else if (current_power_state == POWER_OFF) { OLED_PrintString(72, 48, "待机", &font12x12, OLED_COLOR_NORMAL); } OLED_ShowFrame(); } void Display_ModeSelect(void) { static uint8_t selected_index_state = 0; OLED_NewFrame(); OLED_PrintString(0, 0, "选择工作模式", &font12x12, OLED_COLOR_NORMAL); const char *modes[] = {"自动模式", "节能模式", "手动模式", "定时模式", "时间设置"}; if (selected_index == 4) { for (int i = 0; i < 4; i++) { if (i == selected_index - 1) { OLED_PrintString(0, 12 + i * 12, ">", &font12x12, OLED_COLOR_NORMAL); //闪烁效果 if (selected_index_state == 0) { selected_index_state = 1; OLED_PrintString(12, 12 + i * 12, modes[i + 1], &font12x12, OLED_COLOR_REVERSED); } else { selected_index_state = 0; OLED_PrintString(12, 12 + i * 12, modes[i + 1], &font12x12, OLED_COLOR_NORMAL); } } else { OLED_PrintString(12, 12 + i * 12, modes[i + 1], &font12x12, OLED_COLOR_NORMAL); } if (i == current_mode - 1) { OLED_PrintString(100, 12 + i * 12, "▲", &font12x12, OLED_COLOR_NORMAL); } } } else { for (int i = 0; i < 4; i++) { if (i == selected_index) { OLED_PrintString(0, 12 + i * 12, ">", &font12x12, OLED_COLOR_NORMAL); //闪烁效果 if (selected_index_state == 0) { selected_index_state = 1; OLED_PrintString(12, 12 + i * 12, modes[i], &fonty12x12, OLED_COLOR_NORMAL); } else { selected_index_state = 0; OLED_PrintString(12, 12 + i * 12, modes[i], &font12x12, OLED_COLOR_NORMAL); } } else { OLED_PrintString(12, 12 + i * 12, modes[i], &font12x12, OLED_COLOR_NORMAL); } if (i == current_mode) { OLED_PrintString(100, 12 + i * 12, "▲", &font12x12, OLED_COLOR_NORMAL); } } } OLED_ShowFrame(); } //定时任务列表 void Display_TimerList(void) { static uint8_t blink_state = 0; OLED_NewFrame(); char buf[20]; OLED_PrintString(0, 0, "定时任务列表", &font12x12, OLED_COLOR_NORMAL); for (uint8_t i = 0; i < 3; i++) { sprintf(buf, "定时%d:%02d:%02d %02d℃", i + 1, timers[i].hour, timers[i].minute, timers[i].target_temp); int len = strlen(buf); if (timers[i].state == 1) { sprintf(&buf[len], " %s", "开"); } else { sprintf(&buf[len], " %s", "关"); } if (i == selected_index) { OLED_PrintString(0, 12 + i * 12, ">", &font12x12, OLED_COLOR_NORMAL); //闪烁效果 if (blink_state) { blink_state = 0; OLED_PrintString(8, 12 + i * 12, buf, &font12x12, OLED_COLOR_NORMAL); } else { blink_state = 1; OLED_PrintString(8, 12 + i * 12, buf, &font12x12, OLED_COLOR_REVERSED); } } else { OLED_PrintString(8, 12 + i * 12, buf, &font12x12, OLED_COLOR_NORMAL); } } OLED_ShowFrame(); } void Display_TimerEdit(uint8_t timer_index) { static uint8_t blink_state = 0; OLED_NewFrame(); char buf[20]; sprintf(buf, "编辑定时%d", timer_index + 1); OLED_PrintString(0, 0, buf, &font12x12, OLED_COLOR_NORMAL); sprintf(buf, "开启时间: "); OLED_PrintString(0, 12, buf, &font12x12, OLED_COLOR_NORMAL); if (edit_field == EDIT_TIME) { if (edit_field_state == EDIT_NONE) { sprintf(buf, "%02d:%02d", timers[timer_index].hour, timers[timer_index].minute); OLED_PrintString(60, 12, buf, &font12x12, OLED_COLOR_NORMAL); } else if (edit_field_state == EDIT_HOUR) { sprintf(buf, "%02d", timers[timer_index].hour); if (blink_state == 0) { blink_state = 1; OLED_PrintString(60, 12, buf, &font12x12, OLED_COLOR_REVERSED); } else if (blink_state == 1) { blink_state = 0; OLED_PrintString(60, 12, buf, &font12x12, OLED_COLOR_NORMAL); } OLED_PrintString(72, 12, ":", &font12x12, OLED_COLOR_NORMAL); sprintf(buf, "%02d", timers[timer_index].minute); OLED_PrintString(78, 12, buf, &font12x12, OLED_COLOR_NORMAL); } else if (edit_field_state == EDIT_MINUTE) { sprintf(buf, "%02d", timers[timer_index].hour); OLED_PrintString(60, 12, buf, &font12x12, OLED_COLOR_NORMAL); OLED_PrintString(72, 12, ":", &font12x12, OLED_COLOR_NORMAL); sprintf(buf, "%02d", timers[timer_index].minute); if (blink_state == 0) { blink_state = 1; OLED_PrintString(78, 12, buf, &font12x12, OLED_COLOR_REVERSED); } else if (blink_state == 1) { blink_state = 0; OLED_PrintString(78, 12, buf, &font12x12, OLED_COLOR_NORMAL); } } } else { sprintf(buf, "%02d:%02d", timers[timer_index].hour, timers[timer_index].minute); OLED_PrintString(60, 12, buf, &font12x12, OLED_COLOR_NORMAL); } sprintf(buf, "目标温度: "); OLED_PrintString(0, 24, buf, &font12x12, OLED_COLOR_NORMAL); if (edit_field == EDIT_TEMP) { if (edit_field_state == EDIT_TARGET_TEMP) { sprintf(buf, "%02d", timers[selected_index].target_temp); if (blink_state == 0) { blink_state = 1; OLED_PrintString(60, 24, buf, &font12x12, OLED_COLOR_REVERSED); } else if (blink_state == 1) { blink_state = 0; OLED_PrintString(60, 24, buf, &font12x12, OLED_COLOR_NORMAL); } OLED_PrintString(72, 24, "℃", &font12x12, OLED_COLOR_NORMAL); } else { sprintf(buf, "目标温度: %02d℃", timers[timer_index].target_temp); OLED_PrintString(0, 24, buf, &font12x12, OLED_COLOR_NORMAL); } } else { sprintf(buf, "目标温度: %02d℃", timers[timer_index].target_temp); OLED_PrintString(0, 24, buf, &font12x12, OLED_COLOR_NORMAL); } // 显示操作提示 if (edit_field == EDIT_TIME) { OLED_PrintString(100, 12, "▲", &font12x12, OLED_COLOR_NORMAL); } else if (edit_field == EDIT_TEMP) { OLED_PrintString(100, 24, "▲", &font12x12, OLED_COLOR_NORMAL); } if (timers[timer_index].state == 1) { sprintf(buf, "[开]"); } else { sprintf(buf, "[关]"); } if (edit_field == EDIT_SWITCH) { if (blink_state) { blink_state = 0; OLED_PrintString(50, 36, buf, &font12x12, OLED_COLOR_REVERSED); } else { blink_state = 1; OLED_PrintString(50, 36, buf, &font12x12, OLED_COLOR_NORMAL); } } else { OLED_PrintString(50, 36, buf, &font12x12, OLED_COLOR_NORMAL); } // 显示确定/取消选项 if (edit_field == EDIT_CONFIRM) { if (blink_state) { blink_state = 0; OLED_PrintString(20, 48, "[确定]", &font12x12, OLED_COLOR_REVERSED); } else { blink_state = 1; OLED_PrintString(20, 48, "[确定]", &font12x12, OLED_COLOR_NORMAL); } } else { OLED_PrintString(20, 48, "[确定]", &font12x12, OLED_COLOR_NORMAL); } if (edit_field == EDIT_CANCEL) { if (blink_state) { blink_state = 0; OLED_PrintString(70, 48, "[取消]", &font12x12, OLED_COLOR_REVERSED); } else { blink_state = 1; OLED_PrintString(70, 48, "[取消]", &font12x12, OLED_COLOR_NORMAL); } } else { OLED_PrintString(70, 48, "[取消]", &font12x12, OLED_COLOR_NORMAL); } OLED_ShowFrame(); } void Display_TimeEdit() { static uint8_t blink_state = 0; OLED_NewFrame(); char buf[20]; OLED_PrintString(36, 0, "设置时间", &font12x12, OLED_COLOR_NORMAL); sprintf(buf, "当前时间: "); OLED_PrintString(0, 12, buf, &font12x12, OLED_COLOR_NORMAL); // edit_field = editTimeModeSetTime; // edit_field_state = SET_MINUTE; sprintf(buf, "%02d-%02d-%02d %02d:%02d", setTime.tm_year + 1900, setTime.tm_mon + 1, setTime.tm_mday, setTime.tm_hour, setTime.tm_min); OLED_PrintString(8, 24, buf, &font12x12, OLED_COLOR_NORMAL); if (edit_field == editTimeModeSetTime) { if (blink_state) { blink_state = 0; if (edit_field_state == SET_YEAR) { OLED_DrawLine(8, 36, 31, 36, OLED_COLOR_NORMAL); } else if (edit_field_state == SET_MONTH) { OLED_DrawLine(38, 36, 47, 36, OLED_COLOR_NORMAL); } else if (edit_field_state == SET_DAY) { OLED_DrawLine(56, 36, 66, 36, OLED_COLOR_NORMAL); } else if (edit_field_state == SET_HOUR) { OLED_DrawLine(74, 36, 84, 36, OLED_COLOR_NORMAL); } else if (edit_field_state == SET_MINUTE) { OLED_DrawLine(92, 36, 102, 36, OLED_COLOR_NORMAL); } } else { blink_state = 1; } } // 显示确定/取消选项 if (edit_field == editTimeConfirm) { if (blink_state) { blink_state = 0; OLED_PrintString(20, 48, "[确定]", &font12x12, OLED_COLOR_REVERSED); } else { blink_state = 1; OLED_PrintString(20, 48, "[确定]", &font12x12, OLED_COLOR_NORMAL); } } else { OLED_PrintString(20, 48, "[确定]", &font12x12, OLED_COLOR_NORMAL); } if (edit_field == editTimeCancel) { if (blink_state) { blink_state = 0; OLED_PrintString(70, 48, "[取消]", &font12x12, OLED_COLOR_REVERSED); } else { blink_state = 1; OLED_PrintString(70, 48, "[取消]", &font12x12, OLED_COLOR_NORMAL); } } else { OLED_PrintString(70, 48, "[取消]", &font12x12, OLED_COLOR_NORMAL); } OLED_ShowFrame(); } void System_ProcessInput() { // 检查各个按键事件 KeyEvent up_event = KEY_GetEvent(KEY_ID_UP); KeyEvent down_event = KEY_GetEvent(KEY_ID_DOWN); KeyEvent enter_event = KEY_GetEvent(KEY_ID_ENTER); switch (current_state) { case STATE_MAIN: if (up_event == KEY_EVENT_CLICK) { if (target_temp < MAX_TEMP) { target_temp++; } } else if (down_event == KEY_EVENT_CLICK) { if (target_temp > MIN_TEMP) { target_temp--; } } else if (enter_event == KEY_EVENT_DOUBLE_CLICK) { current_state = STATE_MODE_SELECT; selected_index = current_mode; } break; case STATE_MODE_SELECT: if (up_event == KEY_EVENT_CLICK) { if (selected_index == 0) { selected_index = 4; } else { selected_index--; } } else if (down_event == KEY_EVENT_CLICK) { if (selected_index == 4) { selected_index = 0; } else { selected_index++; } } else if (enter_event == KEY_EVENT_CLICK) { current_state = STATE_MAIN; } else if (enter_event == KEY_EVENT_DOUBLE_CLICK) { if (selected_index == MODE_TIMER) { current_state = STATE_TIMER_LIST; current_mode = selected_index; current_power_state = POWER_OFF; selected_index = 0; } else if (selected_index == STATE_TIME_EDIT) { current_state = STATE_TIME_EDIT; edit_field = EDIT_TIME; edit_field_state = SET_NONE; setTime = nowTime; } else { current_mode = selected_index; } } break; case STATE_TIMER_LIST: if (up_event == KEY_EVENT_CLICK) { if (selected_index == 0) { selected_index = 2; } else { selected_index--; } } else if (down_event == KEY_EVENT_CLICK) { if (selected_index == 2) { selected_index = 0; } else { selected_index++; } } else if (enter_event == KEY_EVENT_CLICK) { current_state = STATE_MODE_SELECT; selected_index = current_mode; } else if (enter_event == KEY_EVENT_DOUBLE_CLICK) { current_state = STATE_TIMER_EDIT; edit_field = EDIT_TIME; edit_field_state = EDIT_NONE; preTimer = timers[selected_index]; } break; case STATE_TIMER_EDIT: if (up_event == KEY_EVENT_CLICK) { if (edit_field == EDIT_TIME) { if (edit_field_state != EDIT_HOUR && edit_field_state != EDIT_MINUTE) { edit_field = EDIT_CANCEL; } else if (edit_field_state == EDIT_HOUR) { timers[selected_index].hour++; if (timers[selected_index].hour > 23) { timers[selected_index].hour = 0; } } else if (edit_field_state == EDIT_MINUTE) { timers[selected_index].minute += 5; if (timers[selected_index].minute > 59) { timers[selected_index].minute = 0; } } } else if (edit_field == EDIT_TEMP) { if (edit_field_state != EDIT_TARGET_TEMP) { edit_field = EDIT_TIME; } else if (edit_field_state == EDIT_TARGET_TEMP) { timers[selected_index].target_temp += 5; if (timers[selected_index].target_temp > MAX_TEMP) { timers[selected_index].target_temp = MIN_TEMP; } } } else if (edit_field == EDIT_SWITCH) { edit_field = EDIT_TEMP; } else if (edit_field == EDIT_CONFIRM) { edit_field = EDIT_SWITCH; } else if (edit_field == EDIT_CANCEL) { edit_field = EDIT_CONFIRM; } } else if (down_event == KEY_EVENT_CLICK) { if (edit_field == EDIT_TIME) { if (edit_field_state != EDIT_HOUR && edit_field_state != EDIT_MINUTE) { edit_field = EDIT_TEMP; } else if (edit_field_state == EDIT_HOUR) { timers[selected_index].hour--; if (timers[selected_index].hour < 0) { timers[selected_index].hour = 23; } } else if (edit_field_state == EDIT_MINUTE) { timers[selected_index].minute -= 5; if (timers[selected_index].minute < 0) { timers[selected_index].minute = 55; } } } else if (edit_field == EDIT_TEMP) { if (edit_field_state != EDIT_TARGET_TEMP) { edit_field = EDIT_SWITCH; } else if (edit_field_state == EDIT_TARGET_TEMP) { timers[selected_index].target_temp -= 5; if (timers[selected_index].target_temp < MIN_TEMP) { timers[selected_index].target_temp = MAX_TEMP; } } } else if (edit_field == EDIT_SWITCH) { edit_field = EDIT_CONFIRM; } else if (edit_field == EDIT_CONFIRM) { edit_field = EDIT_CANCEL; } else if (edit_field == EDIT_CANCEL) { edit_field = EDIT_TIME; edit_field_state = EDIT_NONE; } } else if (enter_event == KEY_EVENT_CLICK) { current_state = STATE_MAIN; timers[selected_index] = preTimer; edit_field = EDIT_TIME; edit_field_state = EDIT_NONE; } else if (enter_event == KEY_EVENT_DOUBLE_CLICK) { if (edit_field == EDIT_TIME) { if (edit_field_state == EDIT_NONE) { edit_field_state = EDIT_HOUR; } else if (edit_field_state == EDIT_HOUR) { edit_field_state = EDIT_MINUTE; } else if (edit_field_state == EDIT_MINUTE) { edit_field_state = EDIT_NONE; } } else if (edit_field == EDIT_TEMP) { if (edit_field_state == EDIT_NONE) { edit_field_state = EDIT_TARGET_TEMP; } else if (edit_field_state == EDIT_TARGET_TEMP) { edit_field_state = EDIT_NONE; } } else if (edit_field == EDIT_SWITCH) { timers[selected_index].state = !timers[selected_index].state; } else if (edit_field == EDIT_CONFIRM) { current_state = STATE_TIMER_LIST; //将状态保存至flash save_timers_to_flash(); } else if (edit_field == EDIT_CANCEL) { //不保存 current_state = STATE_TIMER_LIST; timers[selected_index] = preTimer; edit_field = EDIT_TIME; edit_field_state = EDIT_NONE; } } break; case STATE_TIME_EDIT: if (up_event == KEY_EVENT_CLICK) { if (edit_field == editTimeModeSetTime) { if (edit_field_state == SET_NONE) { edit_field = editTimeCancel; } else if (edit_field_state == SET_YEAR) { setTime.tm_year++; if (setTime.tm_year > 2099) { setTime.tm_year = 0; } } else if (edit_field_state == SET_MONTH) { setTime.tm_mon++; if (setTime.tm_mon > 11) { setTime.tm_mon = 0; } } else if (edit_field_state == SET_DAY) { setTime.tm_mday++; //2月 if (setTime.tm_mon == 1) { if (rtc_is_leap_year(setTime.tm_year + 1900)) { if (setTime.tm_mday > 29) { setTime.tm_mday = 1; } } else { if (setTime.tm_mday > 28) { setTime.tm_mday = 1; } } //大月 } else if (setTime.tm_mon == 0 || setTime.tm_mon == 2 || setTime.tm_mon == 4 || setTime.tm_mon == 6 || setTime.tm_mon == 7 || setTime.tm_mon == 9 || setTime.tm_mon == 11) { if (setTime.tm_mday > 31) { setTime.tm_mday = 1; } //小月 } else { if (setTime.tm_mon > 30) { setTime.tm_mday = 1; } } } else if (edit_field_state == SET_HOUR) { setTime.tm_hour++; if (setTime.tm_hour > 23) { setTime.tm_hour = 0; } } else if (edit_field_state == SET_MINUTE) { setTime.tm_min++; if (setTime.tm_min > 59) { setTime.tm_min = 0; } } } else if (edit_field == editTimeConfirm) { edit_field = editTimeModeSetTime; edit_field_state = SET_NONE; } else if (edit_field == editTimeCancel) { edit_field = editTimeConfirm; } } else if (down_event == KEY_EVENT_CLICK) { if (edit_field == editTimeModeSetTime) { if (edit_field_state == SET_NONE) { edit_field = editTimeConfirm; } else if (edit_field_state == SET_YEAR) { setTime.tm_year--; if (setTime.tm_year < 0) { setTime.tm_year = 2099; } } else if (edit_field_state == SET_MONTH) { setTime.tm_mon--; if (setTime.tm_mon < 0) { setTime.tm_mon = 11; } } else if (edit_field_state == SET_DAY) { setTime.tm_mday--; //2月 if (setTime.tm_mon == 1) { if (rtc_is_leap_year(setTime.tm_year + 1900)) { if (setTime.tm_mday < 1) { setTime.tm_mday = 29; } } else { if (setTime.tm_mday < 1) { setTime.tm_mday = 28; } } //大月 } else if (setTime.tm_mon == 0 || setTime.tm_mon == 2 || setTime.tm_mon == 4 || setTime.tm_mon == 6 || setTime.tm_mon == 7 || setTime.tm_mon == 9 || setTime.tm_mon == 11) { if (setTime.tm_mday < 1) { setTime.tm_mday = 31; } //小月 } else { if (setTime.tm_mon < 1) { setTime.tm_mday = 30; } } } else if (edit_field_state == SET_HOUR) { setTime.tm_hour--; if (setTime.tm_hour < 0) { setTime.tm_hour = 23; } } else if (edit_field_state == SET_MINUTE) { setTime.tm_min--; if (setTime.tm_min < 0) { setTime.tm_min = 59; } } } else if (edit_field == editTimeConfirm) { edit_field = editTimeCancel; } else if (edit_field == editTimeCancel) { edit_field = editTimeModeSetTime; edit_field_state = SET_NONE; } } else if (enter_event == KEY_EVENT_CLICK) { if (edit_field == editTimeModeSetTime) { if (edit_field_state == SET_NONE) { current_state == STATE_MAIN; } else if (edit_field_state == SET_YEAR) { edit_field_state = SET_NONE; } else if (edit_field_state == SET_MONTH) { edit_field_state = SET_NONE; } else if (edit_field_state == SET_DAY) { edit_field_state = SET_NONE; } else if (edit_field_state == SET_HOUR) { edit_field_state = SET_NONE; } else if (edit_field_state == SET_MINUTE) { edit_field_state = SET_NONE; } } else if (edit_field == editTimeConfirm) { current_state == STATE_MAIN; } else if (edit_field == editTimeCancel) { current_state == STATE_MAIN; } } else if (enter_event == KEY_EVENT_DOUBLE_CLICK) { if (edit_field == editTimeModeSetTime) { if (edit_field_state == SET_NONE) { edit_field_state = SET_YEAR; } else if (edit_field_state == SET_YEAR) { edit_field_state = SET_MONTH; } else if (edit_field_state == SET_MONTH) { edit_field_state = SET_DAY; } else if (edit_field_state == SET_DAY) { edit_field_state = SET_HOUR; } else if (edit_field_state == SET_HOUR) { edit_field_state = SET_MINUTE; } else if (edit_field_state == SET_MINUTE) { edit_field_state = SET_NONE; } } else if (edit_field == editTimeConfirm) { //设置时间 KK_RTC_SetTime(&setTime); current_state = STATE_MAIN; } else if (edit_field == editTimeCancel) { current_state = STATE_MAIN; } } } } void System_Display() { switch (current_state) { case STATE_MAIN: Display_MainScreen(); break; case STATE_MODE_SELECT: Display_ModeSelect(); break; case STATE_TIMER_LIST: Display_TimerList(); break; case STATE_TIMER_EDIT: Display_TimerEdit(selected_index); break; case STATE_TIME_EDIT: Display_TimeEdit(); break; default: break; } } // 不定长数据接收完成回调函数 void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size) { static uint8_t preMode = 0; if (huart->Instance == USART2) { if (esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0; //防止串口被刷爆 memcpy(&esp8266_buf[esp8266_cnt], uart2_rx, Size); // HAL_UART_Transmit_IT(&huart1, (uint8_t *) uart2_rx, Size); esp8266_cnt += Size; HAL_UARTEx_ReceiveToIdle_DMA(&huart2, uart2_rx, sizeof(uart2_rx)); __HAL_DMA_DISABLE_IT(huart2.hdmarx, DMA_IT_HT); } else if (huart->Instance == USART3) { HAL_UART_Transmit_IT(&huart1, (uint8_t *) uart3_rx, Size); if (uart3_rx[0] == 'o' && uart3_rx[1] == 'p' && uart3_rx[2] == 'e' && uart3_rx[3] == 'n') {//开启加热 total_power = 1; } else if (uart3_rx[0] == 'c' && uart3_rx[1] == 'l' && uart3_rx[2] == 'o' && uart3_rx[3] == 's' && uart3_rx[4] == 'e') {//关闭加热 total_power = 0; } HAL_UARTEx_ReceiveToIdle_DMA(&huart3, uart3_rx, sizeof(uart3_rx)); __HAL_DMA_DISABLE_IT(huart3.hdmarx, DMA_IT_HT); } } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if (huart->Instance == USART1) { } } void delay_us(uint32_t us) { __HAL_TIM_SET_COUNTER(&htim1, 0); // 重置计数器 while (__HAL_TIM_GET_COUNTER(&htim1) < us); // 等待计数达到目标值 } void TDS_Value_Conversion() { // 获取ADC值 ADC_VALUE = HAL_ADC_GetValue(&hadc1); ADC_ConvertedValueLocal = ((float) ADC_VALUE / 4096 * 3.3) * (1.0 + 0.02 * (temperature_f / 10 - 25.0)); //AD转换 TDS_value = (66.71 * ADC_ConvertedValueLocal * ADC_ConvertedValueLocal * ADC_ConvertedValueLocal - 127.93 * ADC_ConvertedValueLocal * ADC_ConvertedValueLocal + 428.7 * ADC_ConvertedValueLocal) * kValue; if ((TDS_value <= 0)) { TDS_value = 0; } if ((TDS_value > 1400)) { TDS_value = 1400; } } //获取水位 void ObtainTheWaterLevel() { low_water = HAL_GPIO_ReadPin(LOW_WATER_GPIO_Port, LOW_WATER_Pin); high_water = HAL_GPIO_ReadPin(HIGH_WATER_GPIO_Port, HIGH_WATER_Pin); if (low_water == 0 && high_water == 1) { waterlevel = NOMAL_WATER; } else if (low_water == 1 && high_water == 1) { waterlevel = LOW_WATER; } else if (low_water == 0 && high_water == 0) { waterlevel = HIGH_WATER; } } /* 自动模式 */ void Auto_Mode_Handler(void) { if(total_power == 1){ // 简单逻辑:根据时间自动调整目标温度 if (nowTime.tm_hour >= 6 && nowTime.tm_hour <= 9) { // 早高峰 if (temperature_f < target_temp - 5 ) { current_power_state = POWER_ON; } else if (temperature_f >= target_temp) { current_power_state = POWER_OFF; } } else if (nowTime.tm_hour >= 18 && nowTime.tm_hour <= 22) { // 晚高峰 if (temperature_f < target_temp - 5 ) { current_power_state = POWER_ON; } else if (temperature_f >= target_temp) { current_power_state = POWER_OFF; } } else { // 其他时间 if (temperature_f < target_temp - 10) { current_power_state = POWER_ON; } else if (temperature_f >= target_temp) { current_power_state = POWER_OFF; } } }else{ current_power_state = POWER_OFF; } } /* 经济模式 */ void Eco_Mode_Handler() { if(total_power == 1){ // 谷电时段:22:00-6:00 if (nowTime.tm_hour >= 22 || nowTime.tm_hour < 6) { if (temperature_f < target_temp - 5 ) { current_power_state = POWER_ON; } else if (temperature_f >= target_temp) { current_power_state = POWER_OFF; } } else { if (temperature_f < target_temp - 15 ) { current_power_state = POWER_ON; } else if (temperature_f >= target_temp) { current_power_state = POWER_OFF; } } }else{ current_power_state = POWER_OFF; } } /* 定时模式 */ void Timer_Mode_Handler() { static uint32_t flag = 1; if(total_power == 1){ // 检查是否到达定时时间 for (int i = 0; i < 3; i++) { if (nowTime.tm_hour == timers[i].hour && nowTime.tm_min == timers[i].minute && timers[i].state) { target_temp = timers[i].target_temp; flag =0; //开始加热 } } //加热一次就不加热了 if (temperature_f >= target_temp && flag == 0) { current_power_state = POWER_OFF; flag = 1; } if (temperature_f < target_temp && flag == 0) { current_power_state = POWER_ON; } }else{ current_power_state = POWER_OFF; } } /* 手动模式 */ void Manual_Mode_Handler(void) { if(total_power == 1){ if (temperature_f >= target_temp) { current_power_state = POWER_OFF; } else { current_power_state = POWER_ON; } }else{ current_power_state = POWER_OFF; } } //控制加热 void ControlHeating() { switch (current_mode) { case MODE_AUTO://小于目标值10度 则加热 大于等于目标值则关闭 Auto_Mode_Handler(); break; case MODE_ECO: Eco_Mode_Handler(); break; case MODE_MANUAL: Manual_Mode_Handler(); break; case MODE_TIMER: Timer_Mode_Handler(); break; } if(warn_flag){ current_power_state = POWER_OFF; } if (current_power_state == POWER_ON) { HAL_GPIO_WritePin(HEATER_GPIO_Port, HEATER_Pin, GPIO_PIN_RESET); } else if (current_power_state == POWER_OFF) { HAL_GPIO_WritePin(HEATER_GPIO_Port, HEATER_Pin, GPIO_PIN_SET); } } void save_timers_to_flash() { norflash_write((uint8_t *) timers, 0, sizeof(TimerTask) * 3); } void get_timers_from_flash() { norflash_read((uint8_t *) timers, 0, sizeof(TimerTask) * 3); } void led_on(uint8_t on_off){ if(on_off){ HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_RESET); }else{ HAL_GPIO_WritePin(LED0_GPIO_Port, LED0_Pin, GPIO_PIN_SET); } } void Beep_on(uint8_t on_off){ if(on_off){ HAL_GPIO_WritePin(BEEP_GPIO_Port, BEEP_Pin, GPIO_PIN_SET); }else{ HAL_GPIO_WritePin(BEEP_GPIO_Port, BEEP_Pin, GPIO_PIN_RESET); } } void ControlBeep(){ // //水质 // if(TDS_value > 1500){ // led_on(1); // Beep_on(1); // }else{ // led_on(0); // Beep_on(0); // } //水位 if(waterlevel == HIGH_WATER || waterlevel == LOW_WATER || TDS_value > 60 || temperature_f > 65){ led_on(1); Beep_on(1); warn_flag = 1; }else{ led_on(0); Beep_on(0); warn_flag = 0; } // //高温报 // if(temperature_f > 65){ // led_on(1); // Beep_on(1); // }else{ // led_on(0); // Beep_on(0); // } } /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ uint32_t flashsize; uint16_t id = 0; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); MX_USART1_UART_Init(); MX_USART2_UART_Init(); MX_USART3_UART_Init(); MX_RTC_Init(); MX_I2C2_Init(); MX_TIM5_Init(); MX_TIM1_Init(); MX_ADC1_Init(); MX_SPI2_Init(); /* USER CODE BEGIN 2 */ //打开串口中断 HAL_UART_Receive_IT(&huart1, uart1_rx, 2); HAL_UARTEx_ReceiveToIdle_DMA(&huart2, uart2_rx, sizeof(uart2_rx)); __HAL_DMA_DISABLE_IT(huart2.hdmarx, DMA_IT_HT); HAL_UARTEx_ReceiveToIdle_DMA(&huart3, uart3_rx, sizeof(uart3_rx)); __HAL_DMA_DISABLE_IT(huart3.hdmarx, DMA_IT_HT); HAL_Delay(20); // 单片机启动比OLED上电快,需要延迟等待一下 OLED_Init(); norflash_init(); /* 初始化NORFLASH */ HAL_TIM_Base_Start_IT(&htim5);//用于按键检测 HAL_TIM_Base_Start_IT(&htim1);//用于us定时 while (ds18b20_init()) /* DS18B20初始化 */ { printf("DS18B20 INIT ERROR\r\n"); HAL_Delay(500); } id = norflash_read_id(); /* 读取FLASH ID */ while ((id == 0) || (id == 0XFFFF)) /* 检测不到FLASH芯片 */ { printf("FLASH Check Failed!\r\n"); HAL_Delay(300); } printf("SPI FLASH Ready!\r\n"); flashsize = 16 * 1024 * 1024; /* FLASH 大小为16M字节 */ printf("hardware init finish\r\n"); // 启动连续ADC转换 HAL_ADC_Start(&hadc1); // 等待ADC稳定 HAL_Delay(500); get_timers_from_flash(); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ //获取温度 temperature = ds18b20_get_temperature(); temperature_f = temperature / 10 + (temperature % 10 / 10.0); //获取水质 TDS_Value_Conversion(); //获取当前时间 nowTime = *KK_RTC_GetTime(); //获取水位 ObtainTheWaterLevel(); //控制蜂鸣器 ControlBeep(); //控制加热 ControlHeating(); //按键状态及输入处理 System_ProcessInput(); //显示 System_Display(); // printf("temp = %f, power = %d, target_temp = %d\r\n", temperature_f, current_power_state, target_temp); HAL_Delay(300);//500ms刷新一次 if (connectFlag == 0) { Usart_SendString(USART2, (unsigned char *) cmd, strlen((const char *) cmd)); ESP8266_Init(); //ESP8266初始化 } if (connectFlag == 1) { static int cnt = 0; if (cnt == 2) { ESP8266_SendData(); printf("Heart\r\n"); } cnt++; if (cnt > 2) { cnt = 0; } ESP8266_GetIPD(10); } } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_PeriphCLKInitTypeDef PeriphClkInit = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE|RCC_OSCILLATORTYPE_LSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.LSEState = RCC_LSE_ON; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC|RCC_PERIPHCLK_ADC; PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSE; PeriphClkInit.AdcClockSelection = RCC_ADCPCLK2_DIV6; if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK) { Error_Handler(); } } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ 根据这串代码画有YN的流程图,这是基于stm32智能热水器控制系统
05-20
基于STM32 F4的永磁同步电机无位置传感器控制策略研究内容概要:本文围绕基于STM32 F4的永磁同步电机(PMSM)无位置传感器控制策略展开研究,重点探讨在不依赖物理位置传感器的情况下,如何通过算法实现对电机转子位置和速度的精确估计与控制。文中结合嵌入式开发平台STM32 F4,采用如滑模观测器、扩展卡尔曼滤波或高频注入法等先进观测技术,实现对电机反电动势或磁链的估算,进而完成无传感器矢量控制(FOC)。同时,研究涵盖系统建模、控制算法设计、仿真验证(可能使用Simulink)以及在STM32硬件平台上的代码实现与调试,旨在提高电机控制系统的可靠性、降低成本并增强环境适应性。; 适合人群:具备一定电力电子、自动控制理论基础和嵌入式开发经验的电气工程、自动化及相关专业的研究生、科研人员及从事电机驱动开发的工程师。; 使用场景及目标:①掌握永磁同步电机无位置传感器控制的核心原理与实现方法;②学习如何在STM32平台上进行电机控制算法的移植与优化;③为开发高性能、低成本的电机驱动系统提供技术参考与实践指导。; 阅读建议:建议读者结合文中提到的控制理论、仿真模型与实际代码实现进行系统学习,有条件者应在实验平台上进行验证,重点关注观测器设计、参数整定及系统稳定性分析等关键环节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值