В предыдущей части урока мы познакомились со сторожевым таймером, как организован IWDG в контроллере STM32, познакомились со схемой урока, создали и настроили проект.
Откроем файл main.c и для наших светодиодов напишем макросы для удобства дальнейшего написания и чтения кода
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
/* USER CODE BEGIN PV */ /* Private variables ---------------------------------------------------------*/ #define LED1_ON() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_SET) #define LED1_OFF() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_2, GPIO_PIN_RESET) #define LED2_ON() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_SET) #define LED2_OFF() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_3, GPIO_PIN_RESET) #define LED3_ON() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_SET) #define LED3_OFF() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_4, GPIO_PIN_RESET) #define LED4_ON() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_SET) #define LED4_OFF() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_5, GPIO_PIN_RESET) #define LED5_ON() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET) #define LED5_OFF() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET) #define LED6_ON() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_SET) #define LED6_OFF() HAL_GPIO_WritePin(GPIOA, GPIO_PIN_7, GPIO_PIN_RESET) #define LED7_ON() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET) #define LED7_OFF() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET) #define LED8_ON() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET) #define LED8_OFF() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET) #define LED9_ON() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET) #define LED9_OFF() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET) #define LED10_ON() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET) #define LED10_OFF() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_RESET) /* USER CODE END PV */ |
Добавим глобальную переменную, в которой мы будем считать прерывания таймера TIM2
1 2 |
#define LED10_OFF() HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_RESET) uint8_t tim2_count=0; |
В функции main() запустим таймер TIM2
А наш сторожевой таймер отдельно запускать не надо, автоматически добавленный вызов функции MX_IWDG_Init всё уже проделает — и инициализацию и старт.
Мы можем зайти в тело данной функции и посмотреть, что после настроек делителя и счётчика мы вызываем уже функцию HAL_IWDG_Init, тело которой мы также можем посмотреть.
Вот здесь таймер стартует
__HAL_IWDG_START(hiwdg);
Далее мы получаем доступ к регистрам с помощью занесения числа 0x5555 в ключевой регистр.
Затем заносятся значения в регистры
hiwdg->Instance->PR = hiwdg->Init.Prescaler;
hiwdg->Instance->RLR = hiwdg->Init.Reload;
И затем мы перезагружаем счётчик сторожевого таймера занесением в ключевой регистр значения 0xAAAA.
Вернёмся в наш файл main.c и запустим также и таймер 2
1 2 |
/* USER CODE BEGIN 2 */ HAL_TIM_Base_Start_IT(&htim2); |
Добавим обработчик прерываний от таймера, в котором будем считать от 0 до 9, тем самым, анализируя значение нашего глобального счётчика, будем зажигать по очереди наши светодиоды
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
/* USER CODE BEGIN 4 */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if(htim==&htim2) { switch (tim2_count) { case 0: LED10_OFF(); LED1_ON(); break; case 1: LED1_OFF(); LED2_ON(); break; case 2: LED2_OFF(); LED3_ON(); break; case 3: LED3_OFF(); LED4_ON(); break; case 4: LED4_OFF(); LED5_ON(); break; case 5: LED5_OFF(); LED6_ON(); break; case 6: LED6_OFF(); LED7_ON(); break; case 7: LED7_OFF(); LED8_ON(); break; case 8: LED8_OFF(); LED9_ON(); break; case 9: LED9_OFF(); LED10_ON(); break; } tim2_count++; if(tim2_count>9) tim2_count=0; } } /* USER CODE END 4 */ |
Подключим наш программатор к порту USB компьютера, соберём код и попытаемся прошить контроллер.
У нас скорее всего сразу ничего не получится и мы увидим вот такую ошибку
Чтобы справиться с данной ошибкой, зайдём в настройки отладки
Перейдём по закладке в Debugger, где нажмём следующую кнопку
В открывшихся настройках выберем следующий пункт
Сохраним наши настройки. Попробуем ещё раз прошить проект, скорее всего он теперь прошьётся и наши светодиоды начнут мигать по очереди
Но мы вовремя не перезагрузили сторожевой таймер, поэтому, как только отгорит седьмой светодиод и начнёт гореть восьмой, система тут же перезагрузится и процесс возобновится сначала
Чтобы этого не случилось, мы должны заблаговременно перезагрузить счётчик нашего сторожевого таймера IWDG. Перезагрузим его в начале 5 и 10 цикла, тогда программа будет работать непрерывно, если конечно ничего с ней не случится и ничего не повиснет
1 2 |
case 4: HAL_IWDG_Refresh(&hiwdg); |
1 2 |
case 9: HAL_IWDG_Refresh(&hiwdg); |
Соберём код и прошьём контроллер.
Теперь наши светодиоды мигают непрерывно с первого до десятого.
Давайте попробуем перезагрузить наш сторожевой таймер без использования библиотеки HAL. Мы закомментируем соответствующую строку и просто занесём в ключевой регистр нужное число
1 2 |
//HAL_IWDG_Refresh(&hiwdg); IWDG->KR = 0x0000AAAAU; |
Соберём код, прошьём контроллер. У нас по-прежнему всё правильно работает. Я, конечно, советую всё-таки по возможности использовать библиотеку HAL.
Теперь представим, что наш таймер вдруг сломался и перестал считать. У нас тогда по истечении известного интервала после последней перезагрузки сторожевого таймера должна будет перезагрузиться система.
Давайте попробуем данную ситуацию как-то смоделировать.
Для этого добавим обработчик внешнего прерывания от кнопки, в котором мы остановим наш таймер
1 2 3 4 5 6 7 8 9 10 |
//----------------------------------------------- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if(GPIO_Pin== GPIO_PIN_1){ HAL_TIM_Base_Stop_IT(&htim2); } else{ __NOP(); } } /* USER CODE END 4 */ |
Соберём код, прошьём контроллер.
Нажмём на кнопку и наш таймер остановится
Через некоторое время система перезагрузится и код начнёт выполняться с самого начала.
Также можно смоделировать другую ситуацию. Конечно в жизни такая не случится. Мы изменим интервал перезагрузки сторожевого таймера. Мы его сделаем меньше 5 секунд, так как он у нас перезагружается раз в 5 секунд, и он уже не успеет вовремя перезагрузиться, и вместо него опять же перезагрузится контроллер.
Для этого нам достаточно изменить делитель или значение счётчика сторожевого таймера IWDG.
В обработчике прерывания от кнопке закомментируем строку с остановкой таймера TIM2 и вместо этого изменим делитель, сделав его вдвое меньше, тем самым у нас количество секунд в интервале тоже станет вдвое меньше и составит приблизительно 3,68 секунды. Для этого мы будем должны включить в соответствующем регистре только 2-й бит. Не забываем предварительно получить доступ к изменению предделителя, занеся определённый код в ключевой регистр
1 2 3 |
//HAL_TIM_Base_Stop_IT(&htim2); IWDG->KR = 0x00005555U; IWDG->PR = IWDG_PR_PR_2; // IWDG_PRESCALER_64 |
Соберём код, прошьём контроллер, затем в какое-нибудь время нажмём на кнопку кратковременно. Светодиоды скорее всего не перестанут последовательно мигать, если конечно интервал уже не подходит к концу. А через некоторое время контроллер опять перезагрузится и светодиоды начнут бежать сначала, так как у нес интервал изменится и перезагрузка таймера вовремя не произойдёт.
Теперь закомментируем изменение предделителя, а изменим значение счётчика, уменьшив его также вдвое, что будет означать одно и то же и интервал также составит около 3,68 секунды. Запись ключевого значения в соответствующий регистр оставляем, так как для изменения числа в счётчике используется такое же ключевое значение
1 2 |
//IWDG->PR = IWDG_PR_PR_2; // IWDG_PRESCALER_64 IWDG->RLR = 1150; |
Соберём код, прошьём контроллер, затем нажмём кнопку и опять же через некоторое время контроллер перезагрузится из-за несвоевременной перезагрузки IWDG.
Таким образом, в данном занятии мы познакомились с сторожевым таймером (IWDG), поняли, для чего он нужен и закрепили свои знания на практике.
Всем спасибо за внимание!
Предыдущая часть Программирование МК STM32 Следующий урок
Отладочную плату STM32F103C8T6 можно приобрести здесь STM32F103C8T6
Программатор недорогой можно купить здесь ST-Link V2
Смотреть ВИДЕОУРОК (нажмите на картинку)
Добавить комментарий