|
|
|
|
@@ -0,0 +1,376 @@
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
/// @file Display_nhd0420.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_nhd0420.c
|
|
|
|
|
/// @ingroup {group_name}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
// Include files
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
#include "Display.h"
|
|
|
|
|
#include "Logger.h"
|
|
|
|
|
|
|
|
|
|
#include "nhd0420.h"
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
// Constant and macro definitions
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
#define DISPLAY_TASK_STACK_SIZE (2048)
|
|
|
|
|
#define DISPLAY_TRANSMIT_MAX_CHUNK (10)
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
// Type definitions
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
struct displayCharacter
|
|
|
|
|
{
|
|
|
|
|
char character;
|
|
|
|
|
bool isUpdated;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
// File-scope variables
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
static SemaphoreHandle_t displayShadowAccessSemaphore;
|
|
|
|
|
|
|
|
|
|
static struct displayCharacter displayShadow[NHD0420_NUMBER_OF_ROWS][NHD0420_NUMBER_OF_COLUMNS];
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
// 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 printfShadow(void);
|
|
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
// Function definitions
|
|
|
|
|
// -----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
ErrorStatus Display_construct(struct Display* self, void* displayDriver)
|
|
|
|
|
{
|
|
|
|
|
int rowCounter = 0;
|
|
|
|
|
int colCounter = 0;
|
|
|
|
|
ErrorStatus returnValue = SUCCESS;
|
|
|
|
|
|
|
|
|
|
// This is of type NHD0420 - Cast is valid only within this C file
|
|
|
|
|
self->displayDriver = displayDriver;
|
|
|
|
|
|
|
|
|
|
// Clear the display shadow
|
|
|
|
|
for (rowCounter = 0; rowCounter < NHD0420_NUMBER_OF_ROWS; rowCounter++)
|
|
|
|
|
{
|
|
|
|
|
for (colCounter = 0; colCounter < NHD0420_NUMBER_OF_COLUMNS; colCounter++)
|
|
|
|
|
{
|
|
|
|
|
Display_characterUpdate(&displayShadow[rowCounter][colCounter], 0x20);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(returnValue == SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
// Create a semaphore to sync access to the display shadow
|
|
|
|
|
vSemaphoreCreateBinary(displayShadowAccessSemaphore);
|
|
|
|
|
xSemaphoreGive(displayShadowAccessSemaphore);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
self->runTask = true;
|
|
|
|
|
|
|
|
|
|
if(returnValue == SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
if (xTaskCreate(DisplayTask, (const char*)"DisplayTask", DISPLAY_TASK_STACK_SIZE, self, self->TaskPriority, self->taskHandle) != pdTRUE)
|
|
|
|
|
{
|
|
|
|
|
returnValue = ERROR;
|
|
|
|
|
LOGGER_ERROR("Starting display task failed");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
LOGGER_INFO("Display task started");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(returnValue == SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
// If initialisation of module was successful, RESET and adjust the display
|
|
|
|
|
NHD0420_clearScreen(self->displayDriver);
|
|
|
|
|
vTaskDelay(5);
|
|
|
|
|
NHD0420_setContrast(self->displayDriver, 30);
|
|
|
|
|
vTaskDelay(5);
|
|
|
|
|
NHD0420_setBacklightBrightness(self->displayDriver, 3);
|
|
|
|
|
vTaskDelay(5);
|
|
|
|
|
NHD0420_turnOnDisplay(self->displayDriver);
|
|
|
|
|
vTaskDelay(5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return returnValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void Display_destruct(struct Display* self)
|
|
|
|
|
{
|
|
|
|
|
self->runTask = false;
|
|
|
|
|
vSemaphoreDelete(displayShadowAccessSemaphore);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ErrorStatus Display_write(struct Display* self, const char* buffer, unsigned int length, unsigned int row, unsigned int 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 >= NHD0420_NUMBER_OF_ROWS)
|
|
|
|
|
{
|
|
|
|
|
returnValue = ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (returnValue == SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
// Check that the column coordinate does not exceed the display boundary
|
|
|
|
|
if (column - 1 >= NHD0420_NUMBER_OF_COLUMNS)
|
|
|
|
|
{
|
|
|
|
|
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
|
|
|
|
|
// NHD0420_NUMBER_OF_COLUMNS - column >= length
|
|
|
|
|
// must be valid in order to put the requested message on display
|
|
|
|
|
if (NHD0420_NUMBER_OF_COLUMNS - (column - 1) < length)
|
|
|
|
|
{
|
|
|
|
|
returnValue = ERROR;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (returnValue == SUCCESS)
|
|
|
|
|
{
|
|
|
|
|
// Get the access semaphore to the display memory - wait for access
|
|
|
|
|
xSemaphoreTake(displayShadowAccessSemaphore, portMAX_DELAY);
|
|
|
|
|
|
|
|
|
|
int loopCounter;
|
|
|
|
|
for (loopCounter = 0; loopCounter < length; loopCounter++)
|
|
|
|
|
{
|
|
|
|
|
Display_characterUpdate(&displayShadow[row - 1][(column - 1) + loopCounter], buffer[loopCounter]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xSemaphoreGive(displayShadowAccessSemaphore);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
return returnValue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 DisplayTask(void* parameters)
|
|
|
|
|
{
|
|
|
|
|
const struct Display* self = (const struct Display*)parameters;
|
|
|
|
|
const struct NHD0420* displayDriver = (const struct NHD0420*)self->displayDriver;
|
|
|
|
|
|
|
|
|
|
bool leaveLoops = false;
|
|
|
|
|
char buffer[NHD0420_NUMBER_OF_COLUMNS];
|
|
|
|
|
int bufferIndex = 0;
|
|
|
|
|
int rowCounter = 0;
|
|
|
|
|
int colCounter = 0;
|
|
|
|
|
while (self->runTask)
|
|
|
|
|
{
|
|
|
|
|
// Get the access semaphore to the shadow - wait until the write function is finished with updating the shadow
|
|
|
|
|
xSemaphoreTake(displayShadowAccessSemaphore, portMAX_DELAY);
|
|
|
|
|
leaveLoops = false;
|
|
|
|
|
bufferIndex = 0;
|
|
|
|
|
// printfShadow();
|
|
|
|
|
|
|
|
|
|
// Fragment display writing - writing will be done per line
|
|
|
|
|
for (; colCounter < NHD0420_NUMBER_OF_COLUMNS; colCounter++)
|
|
|
|
|
{
|
|
|
|
|
if (Display_isCharacterUpdated(&displayShadow[rowCounter][colCounter]))
|
|
|
|
|
{
|
|
|
|
|
// Found a character that has been updated
|
|
|
|
|
// Put the display cursor at the appropriate coordinates
|
|
|
|
|
NHD0420_setCursorToPosition(displayDriver, rowCounter + 1, colCounter + 1);
|
|
|
|
|
for (bufferIndex = 0; (colCounter < NHD0420_NUMBER_OF_COLUMNS); colCounter++, bufferIndex++)
|
|
|
|
|
{
|
|
|
|
|
// Respect the max number of bytes to transmit
|
|
|
|
|
if (bufferIndex < DISPLAY_TRANSMIT_MAX_CHUNK)
|
|
|
|
|
{
|
|
|
|
|
// Still within the boundaries
|
|
|
|
|
if (Display_isCharacterUpdated(&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(&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(displayShadowAccessSemaphore);
|
|
|
|
|
if (bufferIndex > 0)
|
|
|
|
|
{
|
|
|
|
|
// If there was an update found, send it to the display
|
|
|
|
|
IODevice_write(displayDriver->device, buffer, bufferIndex);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Handle the counters for row and column
|
|
|
|
|
if (colCounter > (NHD0420_NUMBER_OF_COLUMNS - 1))
|
|
|
|
|
{
|
|
|
|
|
// End of row reached - reset column and increment row
|
|
|
|
|
colCounter = 0;
|
|
|
|
|
|
|
|
|
|
if (rowCounter < (NHD0420_NUMBER_OF_ROWS - 1))
|
|
|
|
|
{
|
|
|
|
|
// Increment row
|
|
|
|
|
rowCounter++;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// Last row reached - restart at row 0
|
|
|
|
|
rowCounter = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Task has been marked to end - after leaving the endless loop, end/delete this task
|
|
|
|
|
vTaskDelete(self->taskHandle);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// DEBUG FUNCTION - MIGHT NOT BE NECESSARY LATER ON
|
|
|
|
|
static void printfShadow(void)
|
|
|
|
|
{
|
|
|
|
|
char tempBuffer[21];
|
|
|
|
|
char flagBuffer[21];
|
|
|
|
|
int tempLoop;
|
|
|
|
|
for (tempLoop = 0; tempLoop < 20; tempLoop++)
|
|
|
|
|
{
|
|
|
|
|
tempBuffer[tempLoop] = displayShadow[0][tempLoop].character;
|
|
|
|
|
if (displayShadow[0][tempLoop].isUpdated)
|
|
|
|
|
{
|
|
|
|
|
flagBuffer[tempLoop] = '1';
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
flagBuffer[tempLoop] = '0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
tempBuffer[20] = '\0';
|
|
|
|
|
flagBuffer[20] = '\0';
|
|
|
|
|
|
|
|
|
|
LOGGER_DEBUG("%s -- %s", tempBuffer, flagBuffer);
|
|
|
|
|
for (tempLoop = 0; tempLoop < 20; tempLoop++)
|
|
|
|
|
{
|
|
|
|
|
tempBuffer[tempLoop] = displayShadow[1][tempLoop].character;
|
|
|
|
|
if (displayShadow[1][tempLoop].isUpdated)
|
|
|
|
|
{
|
|
|
|
|
flagBuffer[tempLoop] = '1';
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
flagBuffer[tempLoop] = '0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
tempBuffer[20] = '\0';
|
|
|
|
|
flagBuffer[20] = '\0';
|
|
|
|
|
|
|
|
|
|
LOGGER_DEBUG("%s -- %s", tempBuffer, flagBuffer);
|
|
|
|
|
for (tempLoop = 0; tempLoop < 20; tempLoop++)
|
|
|
|
|
{
|
|
|
|
|
tempBuffer[tempLoop] = displayShadow[2][tempLoop].character;
|
|
|
|
|
if (displayShadow[2][tempLoop].isUpdated)
|
|
|
|
|
{
|
|
|
|
|
flagBuffer[tempLoop] = '1';
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
flagBuffer[tempLoop] = '0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
tempBuffer[20] = '\0';
|
|
|
|
|
flagBuffer[20] = '\0';
|
|
|
|
|
|
|
|
|
|
LOGGER_DEBUG("%s -- %s", tempBuffer, flagBuffer);
|
|
|
|
|
for (tempLoop = 0; tempLoop < 20; tempLoop++)
|
|
|
|
|
{
|
|
|
|
|
tempBuffer[tempLoop] = displayShadow[3][tempLoop].character;
|
|
|
|
|
if (displayShadow[3][tempLoop].isUpdated)
|
|
|
|
|
{
|
|
|
|
|
flagBuffer[tempLoop] = '1';
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
flagBuffer[tempLoop] = '0';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
tempBuffer[20] = '\0';
|
|
|
|
|
flagBuffer[20] = '\0';
|
|
|
|
|
LOGGER_DEBUG("%s -- %s", tempBuffer, flagBuffer);
|
|
|
|
|
}
|