powerpic
Replacement board for a Casio CA-53W
/** @file lcd.c
* LCD Driver for PIC16LF1919x devices using the hardware LCD controller.
*/
#include <xc.h>
#include "drivers/lcd.h"
/** Number of registers that the lcddata spans. */
#define LCD_NUM_DATA_REGISTERS 24
// Each segment value consists of a high byte representing the LCDDATA register
// and a low byte representing the bit number of the segment.
/**
* This matrix holds a value representing each segment's location for the
* primary 8-character display. The character index starts with the left most
* position being 1. A blank character is located at index 0.
*/
static const unsigned int primary_segments[9][7] = {
{0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000},
#if (1 == PCB_REV)
{0x1704, 0x1705, 0x1105, 0x0B04, 0x0B03, 0x1103, 0x1104}, // 1 Left most
{0x1507, 0x0C00, 0x0600, 0x0907, 0x0902, 0x0F02, 0x0F07}, // 2
{0x0E07, 0x1201, 0x0C01, 0x0900, 0x0F01, 0x1501, 0x1500}, // 3
{0x1203, 0x1204, 0x0C04, 0x0603, 0x0602, 0x0C02, 0x0C03}, // 4
{0x1206, 0x1207, 0x0C07, 0x0606, 0x0605, 0x0C05, 0x0C06}, // 5
{0x1300, 0x1301, 0x0D01, 0x0700, 0x0906, 0x0F06, 0x0D00}, // 6
{0x1303, 0x1505, 0x0F05, 0x0703, 0x0702, 0x0D02, 0x0D03}, // 7
{0x1604, 0x1605, 0x1005, 0x0A04, 0x0A06, 0x1006, 0x1004} // 8 Right most
#elif (2 == PCB_REV)
{0x1500, 0x1501, 0x0F01, 0x0900, 0x0807, 0x0E07, 0x0F00},
{0x1705, 0x1104, 0x0B04, 0x0B05, 0x0902, 0x0F02, 0x1105},
{0x0D04, 0x1403, 0x0E03, 0x0B02, 0x1103, 0x1703, 0x1702},
{0x1404, 0x1701, 0x1101, 0x0804, 0x0805, 0x0E05, 0x0E04},
{0x1604, 0x1507, 0x0F07, 0x0A04, 0x0A05, 0x1005, 0x1004},
{0x1201, 0x1202, 0x0C02, 0x0601, 0x0600, 0x0C00, 0x0C01},
{0x1204, 0x1205, 0x0C05, 0x0604, 0x0603, 0x0C03, 0x0C04},
{0x1207, 0x1506, 0x0F06, 0x0607, 0x0606, 0x0C06, 0x0C07}
#endif
};
/**
* This matrix holds a value representing each segment's location for the
* secondary 2.5-character display. The index starts with left character being
* position 1.
*/
static const unsigned int secondary_segments[3][10] = {
{0x0}, // 0 Blank
#if (1 == PCB_REV)
{0x0805, 0x0E05, 0x1404, 0x1606, 0x1406, 0x0E06, 0x1405, // 1 Left
0x0806, 0x1302, 0x0807}, // 3 extra segments for the .5 character.
{0x0803, 0x0A07, 0x1007, 0x1607, 0x1403, 0x0E04, 0x0E03, // 2 Right
0x0804, 0x0000, 0x0000} // This charcter only has 1 extra, so the
// 2 high bits are empty.
#elif (2 == PCB_REV)
{0x0703, 0x0D03, 0x1302, 0x1206, 0x1505, 0x0F05, 0x1303,
0x0905, 0x1203, 0x0704},
{0x0701, 0x0700, 0x0D00, 0x1300, 0x1301, 0x0D02, 0x0D01,
0x0702, 0x0000, 0x0000}
#endif
};
/**
* This array holds a value representing each segment's location for the
* punctuation on the display. The index start with the left most period being 1
* and ending with the colon being 9.
*/
static const unsigned int period_segments[10] = {
0x0, // 0 Blank
#if (1 == PCB_REV)
0x0B05, // 1 Left most
0x0901, // 2
0x0601, // 3
0x0604, // 4
0x0607, // 5
0x0701, // 6
0x0905, // 7
0x0A05, // 8 Right most
0x0F00 // 9th period is the colon.
#elif (2== PCB_REV)
0x0901,
0x0B03,
0x0803,
0x0B01,
0x0907,
0x0602,
0x0605,
0x0906,
0x1102
#endif
};
/**
* This array holds a value representing each segment's location for the
* operation signs on the display. The addition sign is index 1, the subtraction
* sign index 2, the multiplication sign index 3, and the division sign index 4.
*/
static const unsigned int sign_segments[5] = {
0x0,
#if (1 == PCB_REV)
0x1506, // Addition sign
0x1205, // Subtraction sign
0x1202, // Multiplication sign
0x1407 // Division sign
#elif (2 == PCB_REV)
0x1200,
0x1605,
0x1405,
0x1304
#endif
};
/**
* This array holds a value representing each segment's location for the
* misc segments on the display. The chime bell is index 1, the alarm wave thing
* is index 2, the letter K is index 3, PM indicator is index 4, AM indicator
* is index 5.
* TODO: Maybe we should make time of day its own set of functions?
*/
static const unsigned int misc_segments[6] = {
0x0,
#if (1 == PCB_REV)
0x1102, // Bell
0x0B02, // Wave thingy
0x1702, // [O]K
0x1502, // PM
0x1703 // AM
#elif (2 == PCB_REV)
0x0E06,
0x0806,
0x1406,
0x1502,
0x1407
#endif
};
/**
* Pointer to the LCDDATA registers.
* These directly effect the LCD segments. Each COM is 6 1-byte registers, with
* each bit controlling a segment. This pointer allows us to address each
* segment with `lcd_data[x][y]`.
*/
static volatile unsigned char *lcd_data = &LCDDATA0;
/**
* LCD working buffer.
* This buffer holds data to be written to the lcd upon a lcd_update() call.
* This allows a clean screen refresh as the whole lcd can be drawn.
*/
static volatile unsigned char lcd_buffer[LCD_NUM_DATA_REGISTERS];
static void lcd_segments_enable (void);
void
lcd_init (void)
{
# if (1 == PCB_REV)
// Configure controllr to use LFINTOSC
//
LCDCONbits.CS = 0;
# elif (2 == PCB_REV)
// Configure controllr to use SOSC
//
LCDCONbits.CS = 1;
# endif
// Configure controller to use 1/4 mux.
//
LCDCONbits.LMUX = 0x04;
// Configure controller to use only charge pump for lcd voltages.
//
LCDVCON2bits.LCDVSRC = 0x6;
// Charge pump in 5v LP mode with output set to 3.78v
//
LCDVCON1 = 0b11000000;
// Type-A waveform
//
LCDPSbits.WFT = 0;
// Configure prescaler 1:6 to give us 40 hz
//
LCDPSbits.LP = 0b0101;
lcd_segments_clear();
lcd_segments_enable();
// Enable LCD Controller
//
LCDCONbits.LCDEN = 1;
}
void
lcd_update (void)
{
// We start at LCDDATA6 which skips COM0 entirely. This is because COM0 is
// not actually used on the LCD, so we can save some time.
//
for (int lcddata = 6; lcddata < LCD_NUM_DATA_REGISTERS; lcddata++)
{
lcd_data[lcddata] = lcd_buffer[lcddata];
}
}
// Primary
void
lcd_primary_draw (unsigned char position, unsigned char segment)
{
unsigned int segment_address = primary_segments[position][segment];
unsigned char com_byte = (segment_address >> 8);
unsigned char seg_bit_mask = (unsigned char)(1U << (segment_address & 0xF));
lcd_buffer[com_byte] |= seg_bit_mask;
}
void
lcd_primary_clear (unsigned char position, unsigned char segment)
{
unsigned int segment_address = primary_segments[position][segment];
unsigned char com_byte = (segment_address >> 8);
unsigned char seg_bit_mask = (unsigned char)(1U << (segment_address & 0xF));
lcd_buffer[com_byte] &= ~(seg_bit_mask);
}
// Secondary
void
lcd_secondary_draw (unsigned char position, unsigned char segment)
{
unsigned int segment_address = secondary_segments[position][segment];
unsigned char com_byte = (segment_address >> 8);
unsigned char seg_bit_mask = (unsigned char)(1U << (segment_address & 0xF));
lcd_buffer[com_byte] |= seg_bit_mask;
}
void
lcd_secondary_clear (unsigned char position, unsigned char segment)
{
unsigned int segment_address = secondary_segments[position][segment];
unsigned char com_byte = (segment_address >> 8);
unsigned char seg_bit_mask = (unsigned char)(1U << (segment_address & 0xF));
lcd_buffer[com_byte] &= ~(seg_bit_mask);
}
// Punctuation
void
lcd_period_draw (unsigned char segment)
{
unsigned int segment_address = period_segments[segment];
unsigned char com_byte = (segment_address >> 8);
unsigned char seg_bit_mask = (unsigned char)(1U << (segment_address & 0xF));
lcd_buffer[com_byte] |= seg_bit_mask;
}
void
lcd_period_clear (unsigned char segment)
{
unsigned int segment_address = period_segments[segment];
unsigned char com_byte = (segment_address >> 8);
unsigned char seg_bit_mask = (unsigned char)(1U << (segment_address & 0xF));
lcd_buffer[com_byte] &= ~(seg_bit_mask);
}
// Math signs
void
lcd_sign_draw (unsigned char segment)
{
unsigned int segment_address = sign_segments[segment];
unsigned char com_byte = (segment_address >> 8);
unsigned char seg_bit_mask = (unsigned char)(1U << (segment_address & 0xF));
lcd_buffer[com_byte] |= seg_bit_mask;
}
void
lcd_sign_clear (unsigned char segment)
{
unsigned int segment_address = sign_segments[segment];
unsigned char com_byte = (segment_address >> 8);
unsigned char seg_bit_mask = (unsigned char)(1U << (segment_address & 0xF));
lcd_buffer[com_byte] &= ~(seg_bit_mask);
}
// Misc segments
void
lcd_misc_draw (unsigned char segment)
{
unsigned int segment_address = misc_segments[segment];
unsigned char com_byte = (segment_address >> 8);
unsigned char seg_bit_mask = (unsigned char)(1U << (segment_address & 0xF));
lcd_buffer[com_byte] |= seg_bit_mask;
}
void
lcd_misc_clear (unsigned char segment)
{
unsigned int segment_address = misc_segments[segment];
unsigned char com_byte = (segment_address >> 8);
unsigned char seg_bit_mask = (unsigned char)(1U << (segment_address & 0xF));
lcd_buffer[com_byte] &= ~(seg_bit_mask);
}
// All segments
void
lcd_segments_clear (void)
{
for (char lcddata = 0; lcddata < LCD_NUM_DATA_REGISTERS; lcddata++)
{
lcd_data[lcddata] = 0x0;
lcd_buffer[lcddata] = 0x0;
}
}
void
lcd_segments_set (void)
{
for (char lcddata = 0; lcddata < LCD_NUM_DATA_REGISTERS; lcddata++)
{
lcd_data[lcddata] = 0xFF;
lcd_buffer[lcddata] = 0xFF;
}
}
/**
* Enable LCD controller segments.
* This enables the needed segments for COM0-3.
*/
static void
lcd_segments_enable (void)
{
# if (1 == PCB_REV)
// Seg 1-7
//
LCDSE0 = 0xFF;
// Seg 8-11
//
LCDSE1 = 0x0F;
// Seg 19-23
//
LCDSE2 = 0xF8;
// Seg 24 - 26, 29-31
//
LCDSE3 = 0xE7;
// Seg 36 - 39
//
LCDSE4 = 0xF0;
//Seg 42-45
//
LCDSE5 = 0x3C;
# elif (2 == PCB_REV)
LCDSE0 = 0b11111111;
LCDSE1 = 0b00011111;
LCDSE2 = 0b11111000;
LCDSE3 = 0b11100111;
LCDSE4 = 0b00110000;
LCDSE5 = 0b00111111;
# endif
}
// EOF //