/** * @file MoleUsart.c * @brief USARTを使ってprintf()するための実装 * @details * 使用許諾は The MIT License です。 * 参照 https://opensource.org/licenses/mit-license.php * @copyright * Copyright (c) 2020 molelord * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to * deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ #include "main.h" #include <stdlib.h> // abort() #include "molelord/MoleUsart.h" #include "FreeRTOS.h" #include "queue.h" // xQueueCreate() //! MoleUsart型を最大いくつ扱うか #define NUM_OF_MOLEUSART 2 #define TXQ_LENGTH 8 #define RXQ_LENGTH 16 #define QITEM_SIZE sizeof(char) /*! * MoleUsart_IRQHandler()において、LLドライバのUSARTインスタンスの * ポインタ値をキーにしてMoleUsartインスタンスを特定するために、 * 配列にMoleUsartインスタンスへのポインタを記録しておく。 */ static MoleUsart_td *l_inst[NUM_OF_MOLEUSART]; static int32_t l_count = 0; void MoleUsart_construct(MoleUsart_td *inst, USART_TypeDef *usart) { assert_param(0 <= l_count && l_count < NUM_OF_MOLEUSART); inst->usart = usart; inst->txQ = xQueueCreate(TXQ_LENGTH, QITEM_SIZE); if (inst->txQ == NULL) abort(); inst->rxQ = xQueueCreate(RXQ_LENGTH, QITEM_SIZE); if (inst->rxQ == NULL) abort(); int32_t i; for (i = 0; i < NUM_OF_MOLEUSART; i++) { if (l_inst[i] == NULL) { l_inst[i] = inst; goto FOUND; } } abort(); //! l_instの中に空きが見つからなかった FOUND: l_count++; } void MoleUsart_enableIT(MoleUsart_td *inst) { LL_USART_EnableIT_RXNE(inst->usart); } void MoleUsart_destruct(MoleUsart_td *inst) { assert_param(1 <= l_count && l_count <= NUM_OF_MOLEUSART); LL_USART_DisableIT_RXNE(inst->usart); LL_USART_DisableIT_TXE(inst->usart); int32_t i; for (i = 0; i < NUM_OF_MOLEUSART; i++) { if (l_inst[i] == inst) { vQueueDelete(l_inst[i]->rxQ); l_inst[i]->rxQ = NULL; vQueueDelete(l_inst[i]->txQ); l_inst[i]->txQ = NULL; l_inst[i] = NULL; goto FOUND; } } abort(); //! l_instの中に破棄したいものが見つからなかった FOUND: l_count--; } /*! * @note FIFOがいっぱいの時はブロッキングする */ void MoleUsart_putchar(MoleUsart_td *inst, int ch) { const BaseType_t rc = xQueueSend(inst->txQ, &ch, portMAX_DELAY); if (rc != pdTRUE) abort(); /* xQueueSend()を呼び出してから戻ってくるまでの間に、すでに * キューはカラになっている可能性がある。 * キューの残り要素数を取得してから何かするまでの間にISRが呼ばれて * 要素数が変化するのを防ぐために、まずTXE割り込みを禁止し、 * 残り要素数を調べて0ならTXE割り込み禁止のままリターンし、 * そうでないならTXE割り込みを許可する。 */ LL_USART_DisableIT_TXE(inst->usart); const UBaseType_t remain = uxQueueMessagesWaiting(inst->txQ); if (remain == 0) return; LL_USART_EnableIT_TXE(inst->usart); } /*! * @note FIFOがカラの時はブロッキングする */ int MoleUsart_getchar(MoleUsart_td *inst) { char ch = '\0'; const BaseType_t rc = xQueueReceive(inst->rxQ, &ch, portMAX_DELAY); if (rc != pdTRUE) abort(); return ch; } /*! * @details * stm32f?xx_it.cから呼び出させる処理。 * stm32f?xx_it.c側で「複数あるUSARTのうちどれが割り込み要因になったか」 * を特定できるので、それを引数に与える。 */ void MoleUsart_IRQHandler(USART_TypeDef *usart) { //! まず、usartの値をキーとしてMoleUsartインスタンスを特定する int32_t i; for (i = 0; i < NUM_OF_MOLEUSART; i++) { if (l_inst[i] != NULL) { if (l_inst[i]->usart == usart) { goto FOUND; } } } return; // 既知のUSARTでない割り込みだった FOUND: ; MoleUsart_td * const inst = l_inst[i]; BaseType_t higherPriorityTaskWoken = pdFALSE; // RX Not Empty if (LL_USART_IsEnabledIT_RXNE(usart) && LL_USART_IsActiveFlag_RXNE(usart)) { const char ch = LL_USART_ReceiveData8(usart); const BaseType_t rc = xQueueSendFromISR( inst->rxQ, &ch, &higherPriorityTaskWoken); if (rc == errQUEUE_FULL) { /*! * 受信キューがFULLであるということは取りこぼすという * ことなので、設計的にまずい。 * ここでabort()するようなら設計に見直しが必要。 */ abort(); } } // TX Empty if (LL_USART_IsEnabledIT_TXE(usart) && LL_USART_IsActiveFlag_TXE(usart)) { char ch = '\0'; BaseType_t rc = xQueueReceiveFromISR( inst->txQ, &ch, &higherPriorityTaskWoken); /* TXE割り込みが許可されているならば * キューには1つ以上溜まっているはず */ if (rc != pdTRUE) abort(); LL_USART_TransmitData8(usart, (uint8_t)ch); // キューがカラならTXE割り込みを禁止する rc = xQueueIsQueueEmptyFromISR(inst->txQ); if (rc == pdTRUE) { LL_USART_DisableIT_TXE(inst->usart); } } // Transmit Complete if (LL_USART_IsEnabledIT_TC(usart) && LL_USART_IsActiveFlag_TC(usart)) { // TC割り込みをクリアするのみ LL_USART_ClearFlag_TC(usart); } portYIELD_FROM_ISR(higherPriorityTaskWoken); } // vim: tabstop=4 shiftwidth=4 expandtab