Подивимося, що в порівнянні із використанням CMSIS, надає нам STM32CubeMX разом із HAL для організації обробки переривань від зовнішніх джерел, традиційно -- реалізувавши приклад із попереднього CMSIS-поста.
Периферія сконфігурована як і раніше:
- Trig -- пін PA10, вивід. Ми керуватимемо ним третім каналом таймера TIM1.
- Echo -- PA6, генеруємо переривання.
Пам'ятаємо, що треба сконфігурувати таймер -- тактування внутрішнє, ШІМ на третьому каналі, автоматична зупинка (OPM).
Налаштування таймера ті ж, що і в попередньому пості:Клікабельно! |
Конфігурація пінів теж очевидна та (крім імен) встановлюється автоматично:
Клікабельно! |
Клікабельно! |
Шукаємо "галочки" в стовпчику Enabled. :-) Клікабельно! |
Логіка програми залишилася тією ж, хіба що:
- Керування пінами та іншою периферією здійснюється, в основному, звертанням до функцій HAL а не маніпуляцію регістрами.
- Обробники переривань реалізовані в стилі HAL -- заміщено відповідні Callback-функції.
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 | void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_6) { switch (state) { case WAITING_FOR_ECHO_START_S: { echo_start_ticks = TIM1->CNT; state = WAITING_FOR_ECHO_STOP_S; disable_capture_rising(); enable_capture_falling(); break; } case WAITING_FOR_ECHO_STOP_S: { echo_finish_ticks = TIM1->CNT; measured_time = echo_finish_ticks - echo_start_ticks; state = READING_DATA_S; disable_capture_falling(); break; } default: puts("Unexpected signal on EXTI"); // Задовга операція, щоб тут залишати, якщо часто виконуватиметься } } } void HAL_TIM_OC_DelayElapsedCallback(TIM_HandleTypeDef *htim) { if( htim->Instance == TIM1 ) { TIM_CCxChannelCmd(htim->Instance,TIM_CHANNEL_3, TIM_CCx_DISABLE); } } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if( htim->Instance == TIM1 ) { switch (state) { case WAITING_FOR_ECHO_START_S: state = ECHO_TIMEOUT_S; break; case WAITING_FOR_ECHO_STOP_S: state = ECHO_NOT_WENT_LOW_S; break; case IDLE_S: default: ; //puts("Unexpected status"); } } } |
Як видно, логіка їх роботи та ж, що і в попередньому пості, лише немає явної маніпуляції бітом Pending Interrupt.
Перерахування, що описує стан програми, відповідні змінні та функції enable_capture_falling() і т.д. -- такі ж, як і раніше, тому тут їх не повторюватиму. Наведу int main():
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 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | int main(void) { /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ /* MCU Configuration----------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* Configure the system clock */ SystemClock_Config(); /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_TIM1_Init(); /* USER CODE BEGIN 2 */ // Перевіримо, чи лінія Echo в нулі поки ми ще не подали імпульс if (HAL_GPIO_ReadPin(ECHO_EXTI_PA6_GPIO_Port, ECHO_EXTI_PA6_Pin) == GPIO_PIN_SET) { on_error("Error -- Echo line is high, though no impulse was given.", true); } puts("Starting"); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ HAL_NVIC_ClearPendingIRQ(EXTI9_5_IRQn); HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); while (1) { HAL_TIM_PWM_Start_IT(&htim1, TIM_CHANNEL_3); // Біт OPM встановлено, але запускаємо як PWM HAL_NVIC_DisableIRQ(EXTI9_5_IRQn); state = WAITING_FOR_ECHO_START_S; HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); enable_capture_rising(); while ( TIM1->CCER & TIM_CCER_CC3E ) // Не придумав, як тут HAL використати, не ускладнюючи коду додатковими прапорцями... // Поки вивід дозволено -- чекаємо ; if( HAL_GPIO_ReadPin(TRIG_PA10_TIM1_CH3_GPIO_Port, TRIG_PA10_TIM1_CH3_Pin) == GPIO_PIN_SET ) { state = TRIG_NOT_WENT_LOW_S; puts("Trigger does not went low!"); HAL_NVIC_DisableIRQ(EXTI9_5_IRQn); state = IDLE_S; HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); continue; } else { while (state != READING_DATA_S && state != ECHO_TIMEOUT_S && state != ECHO_NOT_WENT_LOW_S); HAL_NVIC_DisableIRQ(EXTI9_5_IRQn); state_t state_copy = state; state = IDLE_S; HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); if ( HAL_GPIO_ReadPin(ECHO_EXTI_PA6_GPIO_Port, ECHO_EXTI_PA6_Pin) == GPIO_PIN_SET || state == ECHO_NOT_WENT_LOW_S) { puts("Echo line does not went low!"); if (state_copy == ECHO_NOT_WENT_LOW_S) puts("\tConfirmed from interrupt"); } if (state_copy == ECHO_TIMEOUT_S) { printf("Echo timeout!\n"); } else { if (measured_time > TOO_FAR_TIMEOUT) { printf("Tooo far -- no echo received, wating for %"PRIu32" mks\n", measured_time); } else { uint32_t distance_mm = (measured_time*10)/58; printf("Distance: %"PRIu32" mm, measured time: %"PRIu32" us\n",distance_mm, measured_time); } } } HAL_Delay(200); /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ } /* USER CODE END 3 */ } |
В принципі, все очевидно. В одному місці, там де очікуємо на кінець імпульсу Trig, (рядок 40), залишено низькорівневий код -- HAL не дає простого способу це зробити, у решті -- менш-більш звичні функції HAL.
Всі використані функції вже було розглянуто в попередніх постах, про таймери та далекоміри. Певний інтерес може представляти хіба згенерована Cube MX_GPIO_Init():
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 | void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct; /* GPIO Ports Clock Enable */ __GPIOD_CLK_ENABLE(); __GPIOA_CLK_ENABLE(); __GPIOC_CLK_ENABLE(); /*Configure GPIO pin : PtPin */ GPIO_InitStruct.Pin = ECHO_EXTI_PA6_Pin; GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING; GPIO_InitStruct.Pull = GPIO_NOPULL; HAL_GPIO_Init(ECHO_EXTI_PA6_GPIO_Port, &GPIO_InitStruct); /*Configure GPIO pins : PCPin PCPin */ GPIO_InitStruct.Pin = BLUE_LED_PC8_Pin|GREEN_LED_PC9_Pin; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Speed = GPIO_SPEED_LOW; HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); /* EXTI interrupt init*/ HAL_NVIC_SetPriority(EXTI9_5_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI9_5_IRQn); } |
В рядку 13 вказується, що функцією піна PA6 буде генерувати переривання у відповідь на фронт сигналу (це стан по замовчуванню -- далі ми явно ним маніпулюватимемо, як описано в попередньому пості).
Власне, все. Доволі просто. Єдине, якби не розуміння деталей на рівні CMSIS, досягнути того ж міркуючи виключно на рівні абстракції HAL, було б багато складніше! Привід задуматися, звичайно. Але не дуже вагомий. :-)
Повний main.c сенсу наводити тут немає -- важливі частини наведені вище, а кому цікаво -- див. проект. Скачати його, проект, можна тут.
На разі, із ультразвуковим далекоміром, нарешті -- все,
Дякую за увагу!
Немає коментарів:
Дописати коментар