// ----------------------------------------------------------------------------- /// @file Display.c /// @brief Description // ----------------------------------------------------------------------------- // Micro-Key bv // Industrieweg 28, 9804 TG Noordhorn // Postbus 92, 9800 AB Zuidhorn // The Netherlands // Tel: +31 594 503020 // Fax: +31 594 505825 // Email: support@microkey.nl // Web: www.microkey.nl // ----------------------------------------------------------------------------- /// $Revision$ /// $Author$ /// $Date$ // (c) 2017 Micro-Key bv // ----------------------------------------------------------------------------- /// @file Display.c /// @ingroup {group_name} // ----------------------------------------------------------------------------- // Include files // ----------------------------------------------------------------------------- #include "Display.h" #include "Logger.h" #include "nhd0420.h" #include "platform.h" // ----------------------------------------------------------------------------- // Constant and macro definitions // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // Type definitions // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // File-scope variables // ----------------------------------------------------------------------------- // ----------------------------------------------------------------------------- // Function declarations // ----------------------------------------------------------------------------- static void DisplayTask(void* parameters); inline static void Display_characterUpdate (struct DisplayCharacter* displayCharacter, char character); inline static bool Display_isCharacterUpdated (struct DisplayCharacter* displayCharacter); inline static char Display_getUpdatedCharacter (struct DisplayCharacter* displayCharacter); static void Display_characterUpdateAll(struct Display* self); // ----------------------------------------------------------------------------- // Function definitions // ----------------------------------------------------------------------------- ErrorStatus Display_construct(struct Display* self, struct DisplayDevice* displayDevice, int TaskPriority, uint16_t stackSize, int maxCharactersPerTransmit, int refreshFeedFrequency_ms, int refreshPeriod) { ErrorStatus returnValue = SUCCESS; self->displayDevice = displayDevice; self->TaskPriority = TaskPriority; self->stackSize = stackSize; self->maxCharactersPerTransmit = maxCharactersPerTransmit; self->refreshFeedCounter = 0; self->refreshFeedFrequency_ms = refreshFeedFrequency_ms; self->refreshPeriod_ms = refreshPeriod; if(returnValue == SUCCESS) { // Create a semaphore to sync access to the display shadow vSemaphoreCreateBinary(self->displayShadowAccessSemaphore); xSemaphoreGive(self->displayShadowAccessSemaphore); // Clear the display shadow size_t rowCounter; size_t colCounter; for (rowCounter = 0; rowCounter < self->displayDevice->parameters.numberOfRows; rowCounter++) { for (colCounter = 0; colCounter < self->displayDevice->parameters.numberOfColumns; colCounter++) { Display_characterUpdate(&self->displayShadow[rowCounter][colCounter], 0x20); } } } self->runTask = true; if(returnValue == SUCCESS) { if (xTaskCreate(DisplayTask, (const char*)"DisplayTask", self->stackSize, self, self->TaskPriority, self->taskHandle) != pdTRUE) { returnValue = ERROR; LOGGER_ERROR(mainLog, "Starting display task failed"); } else { LOGGER_INFO(mainLog, "Display task started"); } } return returnValue; } void Display_destruct(struct Display* self) { self->runTask = false; vSemaphoreDelete(self->displayShadowAccessSemaphore); } ErrorStatus Display_clearScreen(struct Display* self) { return DisplayDevice_clear(self->displayDevice); } ErrorStatus Display_setState(struct Display* self, DisplayDevice_functionalState state) { return DisplayDevice_setState(self->displayDevice, state); } ErrorStatus Display_setBrightness(struct Display* self, size_t brightness) { return DisplayDevice_setBrightness(self->displayDevice, brightness); } ErrorStatus Display_setContrast(struct Display* self, size_t contrast) { return DisplayDevice_setContrast(self->displayDevice, contrast); } ErrorStatus Display_write(struct Display* self, const char* buffer, unsigned int length, size_t row, size_t column) { ErrorStatus returnValue = SUCCESS; // Prior to any action on the display memory, perform necessary checkings if (returnValue == SUCCESS) { // Check that the row coordinate does not exceed the display boundary if (row - 1 >= self->displayDevice->parameters.numberOfRows) { returnValue = ERROR; } } if (returnValue == SUCCESS) { // Check that the column coordinate does not exceed the display boundary if (column - 1 >= self->displayDevice->parameters.numberOfColumns) { returnValue = ERROR; } } if (returnValue == SUCCESS) { // Check that the length request does not exceed the display boundary // This is checked in combination with the column coordinate // numberOfColumns - column >= length // must be valid in order to put the requested message on display if (self->displayDevice->parameters.numberOfColumns - (column - 1) < length) { returnValue = ERROR; } } if (returnValue == SUCCESS) { // Get the access semaphore to the display memory - wait for access xSemaphoreTake(self->displayShadowAccessSemaphore, portMAX_DELAY); int loopCounter; for (loopCounter = 0; loopCounter < length; loopCounter++) { Display_characterUpdate(&self->displayShadow[row - 1][(column - 1) + loopCounter], buffer[loopCounter]); } xSemaphoreGive(self->displayShadowAccessSemaphore); } return returnValue; } void Display_feedRefreshCounter(struct Display* self) { self->refreshFeedCounter++; } inline static void Display_characterUpdate (struct DisplayCharacter* displayCharacter, char character) { displayCharacter->character = character; displayCharacter->isUpdated = true; } inline static bool Display_isCharacterUpdated (struct DisplayCharacter* displayCharacter) { return displayCharacter->isUpdated; } inline static char Display_getUpdatedCharacter (struct DisplayCharacter* displayCharacter) { displayCharacter->isUpdated = false; return displayCharacter->character; } static void Display_characterUpdateAll(struct Display* self) { size_t rowCounter; size_t colCounter; // Get the access semaphore to the shadow - wait until the other functions are finished with updating the shadow xSemaphoreTake(self->displayShadowAccessSemaphore, portMAX_DELAY); // Clear the display shadow for (rowCounter = 0; rowCounter < self->displayDevice->parameters.numberOfRows; rowCounter++) { for (colCounter = 0; colCounter < self->displayDevice->parameters.numberOfColumns; colCounter++) { self->displayShadow[rowCounter][colCounter].isUpdated = true; } } // Give back display memory access semaphore to allow new updates xSemaphoreGive(self->displayShadowAccessSemaphore); } static void DisplayTask(void* parameters) { struct Display* self = (struct Display*)parameters; bool leaveLoops = false; char buffer[NHD0420_NUMBER_OF_COLUMNS]; int bufferIndex = 0; int rowCounter = 0; int colCounter = 0; size_t rowStart; size_t columnStart; while (self->runTask) { // Get the access semaphore to the shadow - wait until the other functions are finished with updating the shadow xSemaphoreTake(self->displayShadowAccessSemaphore, portMAX_DELAY); leaveLoops = false; bufferIndex = 0; // Fragment display writing - writing will be done per line for (; colCounter < self->displayDevice->parameters.numberOfColumns; colCounter++) { if (Display_isCharacterUpdated(&self->displayShadow[rowCounter][colCounter])) { // Found a character that has been updated // Put the display cursor at the appropriate coordinates rowStart = rowCounter + 1; columnStart = colCounter + 1; for (bufferIndex = 0; (colCounter < self->displayDevice->parameters.numberOfColumns); colCounter++, bufferIndex++) { // Respect the max number of bytes to transmit if (bufferIndex < self->maxCharactersPerTransmit) { // Still within the boundaries if (Display_isCharacterUpdated(&self->displayShadow[rowCounter][colCounter])) { // Current character has been updated and must be sent to display // But data from display shadow to transmit buffer buffer[bufferIndex] = Display_getUpdatedCharacter(&self->displayShadow[rowCounter][colCounter]); } else { // Current character is already on the display // Stop scanning for more updated characters here and leave the loops in order // to start transmission to the display leaveLoops = true; break; } } else { // Max number of characters reached // Stop scanning for more updated characters here and leave the loops in order // to start transmission to the display leaveLoops = true; break; } } } // Check if loop should be left if (leaveLoops) { // An inner loop decided to leave, so leave break; } } // Give back display memory access semaphore to allow new updates xSemaphoreGive(self->displayShadowAccessSemaphore); if (bufferIndex > 0) { // If there was an update found, send it to the display DisplayDevice_write(self->displayDevice, buffer, bufferIndex, rowStart, columnStart); } // Handle the counters for row and column if (colCounter > (self->displayDevice->parameters.numberOfColumns - 1)) { // End of row reached - reset column and increment row colCounter = 0; if (rowCounter < (self->displayDevice->parameters.numberOfRows - 1)) { // Increment row rowCounter++; } else { // Last row reached - restart at row 0 rowCounter = 0; } } // Check for display refresh timings if (self->refreshFeedCounter * self->refreshFeedFrequency_ms > self->refreshPeriod_ms) { self->refreshFeedCounter = 0; Display_characterUpdateAll(self); } vTaskDelay(10); } // Task has been marked to end - after leaving the endless loop, end/delete this task vTaskDelete(self->taskHandle); }