Files
hsb/S - Software/0 - HSB MRTS Kathode-MCP/3 - Implementation/0 - Code/hsb-mrts/src/Display.c
2017-10-10 12:46:41 +00:00

392 lines
13 KiB
C

// -----------------------------------------------------------------------------
/// @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;
if (!self->initialized)
{
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);
// Create a semaphore to sync writing requests to the display
vSemaphoreCreateBinary(self->displayWriteRequest);
// 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");
self->initialized = true;
}
}
}
else
{
returnValue = ERROR;
}
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_clearLine(struct Display* self, size_t line)
{
return DisplayDevice_clearLine(self->displayDevice, line);
}
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;
if (self->initialized)
{
// 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);
xSemaphoreGive(self->displayWriteRequest);
}
}
else
{
returnValue = ERROR;
}
return returnValue;
}
void Display_feedRefreshCounter(struct Display* self)
{
if (self->initialized)
{
self->refreshFeedCounter++;
}
}
inline static void Display_characterUpdate (struct DisplayCharacter* displayCharacter, char character)
{
// Sending the same character should not lead to an updated character
if (displayCharacter->character != 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);
xSemaphoreGive(self->displayWriteRequest);
}
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)
{
// Wait until a write or refresh function has requested this task to write to the display
// xSemaphoreTake(self->displayWriteRequest, portMAX_DELAY);
// for (rowCounter = 0; rowCounter < self->displayDevice->parameters.numberOfRows; rowCounter++)
{
// 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);
}
vTaskDelay(10);
}
// Task has been marked to end - after leaving the endless loop, end/delete this task
vTaskDelete(self->taskHandle);
}