W tym poście opiszę wykorzystywanie mechanizmu task notification zamiast semafora binarnego dla przerwań NVIC.
[Źródło: https://www.st.com/content/st_com/en/products/evaluation-tools/product-evaluation-tools/mcu-mpu-eval-tools/stm32-mcu-mpu-eval-tools/stm32-nucleo-boards/nucleo-h753zi.html#overview]
Task Notification:
Task Notification generuje zdarzenie, które jest przekazywane bezpośrednio do zadania. Gdy jest uruchomione ustawia stan zdarzenia na true.
Korzystanie z Task Notification zamiast semafora czy kolejki, pozwala na ograniczenie zużycia pamięci RAM oraz przyśpieszeniu działania o 45%. Zależy to oczywiście od odpowiedniej implementacji. Po więcej szczegółów można sięgnąć do dokumentacji umieszczonej na samym dole postu.
Program:
Podobnie jak w poprzednim przykładzie przykład przedstawię w oparciu o przerwania NVIC wykorzystywane do odczytu danych z interfejsu Wiegand.
- void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) {
- Wiegand1_ReadData(GPIO_Pin);
- Wiegand2_ReadData(GPIO_Pin);
- }
- void Wiegand1_ReadData(const uint16_t gpio_pin) {
- BaseType_t xHigherPriorityTaskWoken = pdFALSE;
- if(gpio_pin == GPIO_PIN_13) {
- configASSERT( xTaskToNotifyat_Wieg1_W0 != NULL );
- vTaskNotifyGiveFromISR( xTaskToNotifyat_Wieg1_W0, &xHigherPriorityTaskWoken );
- portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
- }
- if(gpio_pin == GPIO_PIN_14) {
- configASSERT( xTaskToNotifyat_Wieg1_W1 != NULL );
- vTaskNotifyGiveFromISR( xTaskToNotifyat_Wieg1_W1, &xHigherPriorityTaskWoken );
- portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
- }
- }
- void Wiegand2_ReadData(const uint16_t gpio_pin) {
- BaseType_t xHigherPriorityTaskWoken = pdFALSE;
- if(gpio_pin == GPIO_PIN_12) {
- configASSERT( xTaskToNotifyat_Wieg2_W0 != NULL );
- vTaskNotifyGiveFromISR( xTaskToNotifyat_Wieg2_W0, &xHigherPriorityTaskWoken );
- portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
- }
- if(gpio_pin == GPIO_PIN_11) {
- configASSERT( xTaskToNotifyat_Wieg2_W1 != NULL );
- vTaskNotifyGiveFromISR( xTaskToNotifyat_Wieg2_W1, &xHigherPriorityTaskWoken );
- portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
- }
- }
Powyżej opis wywołania zadania z przerwania. Funkcja vTaskNotifyFromISR. Jako argument podawany jest handler do xTaskToNotify. Drugi argument to pxHigherPriorityTaskWoken. Jego wartość musi być ustawiona na zero. Zostanie ona ustawiona na jeden (true) jeśli zadanie zostanie odblokowane oraz jego priorytet będzie wyższy niż aktualnie wykonywanego zdarzenia.
- osThreadDef(wiegand1TaskW0, TaskWiegand1ListenW0, osPriorityNormal, 0, 128 * 4);
- taskWiegand1_W0 = osThreadCreate(osThread(wiegand1TaskW0), NULL);
- osThreadDef(wiegand1TaskW1, TaskWiegand1ListenW1, osPriorityNormal, 0, 128 * 4);
- taskWiegand1_W1 = osThreadCreate(osThread(wiegand1TaskW1), NULL);
- osThreadDef(wiegand2TaskW0, TaskWiegand2ListenW0, osPriorityNormal, 0, 128 * 4);
- taskWiegand2_W0 = osThreadCreate(osThread(wiegand2TaskW0), NULL);
- osThreadDef(wiegand2TaskW1, TaskWiegand2ListenW1, osPriorityNormal, 0, 128 * 4);
- taskWiegand2_W1 = osThreadCreate(osThread(wiegand2TaskW1), NULL);
Powyżej uruchomienie zadań odbioru danych od pinów z NVIC.
- void TaskWiegand1ListenW0(void const *argument) {
- configASSERT(xTaskToNotifyat_Wieg1_W0 == NULL);
- xTaskToNotifyat_Wieg1_W0 = xTaskGetCurrentTaskHandle();
- for(;;) {
- uint32_t ulNotificationValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
- if(ulNotificationValue == 1) {
- Wiegand_GetData(&wiegandCardData_Head1, 0);
- }
- else { }
- }
- }
- void TaskWiegand1ListenW1(void const *argument) {
- configASSERT(xTaskToNotifyat_Wieg1_W1 == NULL);
- xTaskToNotifyat_Wieg1_W1 = xTaskGetCurrentTaskHandle();
- for(;;) {
- uint32_t ulNotificationValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
- if(ulNotificationValue == 1) {
- Wiegand_GetData(&wiegandCardData_Head1, 1);
- }
- else { }
- }
- }
- void TaskWiegand2ListenW0(void const *argument) {
- configASSERT(xTaskToNotifyat_Wieg2_W0 == NULL);
- xTaskToNotifyat_Wieg2_W0 = xTaskGetCurrentTaskHandle();
- for(;;) {
- uint32_t ulNotificationValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
- if(ulNotificationValue == 1) {
- Wiegand_GetData(&wiegandCardData_Head2, 0);
- }
- else { }
- }
- }
- void TaskWiegand2ListenW1(void const *argument) {
- configASSERT(xTaskToNotifyat_Wieg2_W1 == NULL);
- xTaskToNotifyat_Wieg2_W1 = xTaskGetCurrentTaskHandle();
- for(;;) {
- uint32_t ulNotificationValue = ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
- if(ulNotificationValue == 1) {
- Wiegand_GetData(&wiegandCardData_Head2, 1);
- }
- else { }
- }
- }
Obsługa odebrania danych z każdego z pinów obsługiwana jest przez osobne zdarzenie. Łączenie danych odbywa się tutaj:
- void StartReaderTask(void const * argument)
- {
- for(;;)
- {
- if(wiegand_checkReceivedData(&wiegandCardData_Head1, settingsData.wiegSett_Data_Flags) == 1) {
- wiegand_ClearData_PassItToRegistration(&wiegandCardData_Head1, ®istrData);
- wiegand_initStructureWithDefaultValues(&wiegandCardData_Head1);
- }
- if(wiegand_checkReceivedData(&wiegandCardData_Head2, settingsData.wiegSett_Data_Flags) == 1) {
- wiegand_ClearData_PassItToRegistration(&wiegandCardData_Head2, ®istrData);
- wiegand_initStructureWithDefaultValues(&wiegandCardData_Head2);
- }
- osDelay(1000);
- }
- }