/*=============================================================================
* File: dogm16x.c
* Created: September 12, 2010 [v.1.0]
* Last change: September 3, 2011 [v.1.4]
* Author: Fredrik Jonsson
*
* Copyright (C) 2011, Fredrik Jonsson
* Non-commercial copying welcome.
*
* Summary
*
* The set of routines in dogm16x.c provides a driver for the Electronic
* Assembly DOG-M family of LCD displays [1,2] for the Microchip PIC32
* microcontroller, employing the Parallel Master Port (PMP), with data
* on pins PMD0-PMD7 and PMP control signals on pins PMWR, PMRD and PMA0.
*
* Hardware connections
*
* The connections between the Parallel Master Port (PMP, data on pins
* PMD0-PMD7; PMP control signals on pins PMWR, PMRD, and PMA0) of the
* PIC32MX processor and the Electronic Assembly DOGM display are as
* follows:
*
* PIC32 pin OLIMEX PCB LCD pin
* --------------------------------------------------------------------------
* PMD7 --- RE7 --- 28 (D7) Display data, MSB
* PMD6 --- RE6 --- 29 (D6) Display data
* PMD5 --- RE5 --- 30 (D5) Display data
* PMD4 --- RE4 --- 31 (D4) Display data
* PMD3 --- RE3 --- 32 (D3) Display data
* PMD2 --- RE2 --- 33 (D2) Display data
* PMD1 --- RE1 --- 34 (D1) Display data
* PMD0 --- RE0 --- 35 (D0) Display data, LSB
* PMWR --- RD4 --- 36 (E) Enable, falling edge
* PMRD --- RD5 --- 37 (R/W) Low=Write, High=Read
* PMA0 --- RB15 --- 39 (RS) Low=Control, High=Data
*
* In addition, the following connections to the LCD provide power supply and
* proper ground:
* PIC32 pin OLIMEX PCB LCD pin
* GROUND --- 38 (CSB) Chip select
* GROUND --- 27 (VSS) Power supply, 0V
* +3.3V --- 26 (VDD) Power supply, +3.3V
* +3.3V --- 25 (VIN)
* +3.3V --- 23 (PSB)
* +3.3V --- 40 (RESET)
* +3.3V --- X ohm resistor --- 20 Power supply 3.3V, illumination
* +3.3V --- X ohm resistor --- 1 Power supply 3.3V, illumination
* GROUND --- 2 (VSS) Power supply 0V, illumination
* GROUND --- 19 (VSS) Power supply 0V, illumination
* |------------ 21 (CAP1N)
* === 2.2 uF Capacitor
* |------------ 22 (CAP1P)
* |------------ 24 (VOUT)
* === 2.2 uF
* +3.3V -------|
*
* Installing the driver as a shared library
*
* See enclosed file README.txt.
*
* Examples of usage
*
* Example 1. High-level usage of the library, writing entire strings of data:
*
* char buf[128];
* float x=0.4711;
* write_string_to_display("Hello World!\n");
* sprintf(buf,"sin(%f)=%f\n",x,sin(x));
* write_string_to_display(buf);
*
* Example 2. Low-level usage of the library, writing individual characters
* and positioning the cursor by direct access to the LCD registers:
*
* write_to_display_register(LCDCMD,0x80|0x00); // Set position to 1st row, 1st col
* write_to_display_register(LCDDATA,'A'); // Write 'A' at 1st row, 1st col
* write_to_display_register(LCDCMD,0x80|0x00); // Reposition to 1st row, 1st col
* write_to_display_register(LCDDATA,'B'); // Overwrites the previous 'A'
* write_to_display_register(LCDDATA,'C'); // Writes a 'C' next after the 'B'
* write_to_display_register(LCDCMD,0x80|0x05); // Reposition to 1st row, 6th col
* write_to_display_register(LCDDATA,'D'); // Writes a 'C' at 1st row, 5th col
* write_to_display_register(LCDDATA,'E'); // Writes a 'E' next after the 'D'
* write_to_display_register(LCDCMD,0x80|0x10); // Set position to 2nd row, 1st col
* write_to_display_register(LCDDATA,'G'); // Write 'G' at 1st row, 1st col
* sprintf(buf,"%d",read_display_register(0x02)&0x7f);
* write_string_to_display(buf);
*
*
* [1] See the Electronic Assembly web site on their display-on-glass (DOG)
* series at http://www.lcd-module.com/products/dog.html
* [2] For suppliers of the Electronic Assembly DOG-M series and corresponding
* backlights, see https://www.elfa.se/elfa3~se_en/elfa/init.do?query=DC-76206&in=fam
* (display) and https://www.elfa.se/elfa3~se_en/elfa/init.do?query=DC-76207&in=fam
* (backlight modules).
*===========================================================================*/
#include /* Microchip PIC32-family specifics/generics */
#include /* To access the PIC32 Peripheral Library */
#include "dogm16x.h"
/*
* Directly from the PIC32 Family Reference Manual [1]: The Parallel Master
* Port (PMP) is a parallel 8-bit/16-bit I/O module specifically designed to
* communicate with a wide variety of parallel devices such as communications
* peripherals, LCDs, external memory devices and microcontrollers. Because
* the interfaces to parallel peripherals vary significantly, the PMP module
* is highly configurable.
*
* The driver makes use of the Parallell Master Port (PMP) in Master mode (mode
* set by PMP_MASTER_1), in which the following Special Function Registers
* (SFRs) apply:
*
* PMCON: Parallel Port Control Register
* This register (Register 13-1) contains the bits that control much of the
* module?s basic functionality. A key bit is the ON control bit, which is
* used to Reset, enable or disable the module. When the module is disabled,
* all of the associated I/O pins revert to their designated I/O function.
* In addition, any read or write operations active or pending are stopped,
* and the BUSY bit is cleared. The data within the module registers is
* retained, including the data in PMSTAT register. Therefore, the module
* could be disabled after a reception, and the last received data and status
* would still be available for processing. When the module is enabled, all
* buffer control logic is reset, along with PMSTAT. All other bits in PMCON
* control address multiplexing, enable various port control signals, and
* select control signal polarity. For futher details, see PIC32 Family
* Reference Manual [1] Chapter 13.3.1 ?Parallel Master Port Configuration
* Options?.
*
* PMMODE: Parallel Port Mode Register
* This register (Register 13-2) contains bits that control the operational
* modes of the module. Master/Slave mode selection, as well as configuration
* options for both modes, are set by this register. It also contains the
* universal status flag BUSY, used in Master modes to indicate that an
* operation by the module in progress. For futher details, see PIC32 Family
* Reference Manual [1], Chapters 13.4 ?Slave Modes of Operation? and 13.3
* ?Master Modes of Operation?.
*
* PMADDR: Parallel Port Address Register
* This register (Register 13-3) functions as PMADDR in master modes. It
* contains the address to which outgoing data is to be written, as well
* as the Chip Select control bits for addressing parallel slave devices.
* The PMADDR register is not used in any of the Slave modes.
*
* PMAEN: Parallel Port Pin Enable Register
* This register (Register 13-6) controls the operation of address and Chip
* Select pins associated to this module. Setting these bits allocates the
* corresponding microcontroller pins to the PMP module; clearing the bits
* allocates the pins to port I/O or other peripheral modules associated
* with the pin.
*/
/*
* In order to avoid any conflicts with these settings (via pragmas),
* ensure that the MPLAB IDE settings are Configure -> Configuration
* Bits -> Configuration Bits set in code.
*
* For details on these config settings for the oscillator frequency
* dividers of the PIC32MX, see Di Lucio page 151.
*/
#pragma config POSCMOD=XT, FNOSC=PRIPLL
#pragma config FPLLIDIV=DIV_2, FPLLMUL=MUL_18, FPLLODIV=DIV_1
#pragma config FPBDIV=DIV_2, FWDTEN=OFF, CP=OFF, BWP=OFF
#undef FPB
#define FPB (36000000L)
void delayms(unsigned t)
{
int fpbthresh=FPB/1000;
T1CON = 0x8000; /* Enable TMR1, Tpb, 1:1 */
while (t--) { /* t x 1ms loop */
TMR1 = 0;
while (TMR1 < fpbthresh);
}
}
#undef FPB
int row, col;
/*
* The initialize_display() routine performs the initialization of the Parallel Master
* Port (PMP) key parameters of the Microchip PIC32MX (here PIC32MX340F512)
* using the mPMPOpen() macro as defined in pmp.h [1].
*
* The significance of the bits of the PMCON (Parallel Port Control Register)
* are as follows:
*
* bit 31-16 Reserved: Write ?0?; ignore read
*
* bit 15 ON: Parallel Master Port Enable bit
* 1 = PMP enabled
* 0 = PMP disabled, no off-chip access performed
* Note: When using 1:1 PBCLK divisor, the user?s software should not read/write
* the peripheral?s SFRs in the SYSCLK cycle immediately following the instruction
* that clears the module?s ON control bit.
*
* bit 14 FRZ: Freeze in Debug Exception Mode bit
* 1 = Freeze operation when CPU is in Debug Exception mode
* 0 = Continue operation even when CPU is in Debug Exception mode
* Note: FRZ is writable in Debug Exception mode only, it is forced to ?0? in normal mode.
*
* bit 13 SIDL: Stop in Idle Mode bit
* 1 = Discontinue module operation when device enters Idle mode
* 0 = Continue module operation in Idle mode
*
* bit 12-11 ADRMUX<1:0>: Address/Data Multiplexing Selection bits
* 11 = All 16 bits of address are multiplexed on PMD<15:0> pins
* 10 = All 16 bits of address are multiplexed on PMD<7:0> pins
* 01 = Lower 8 bits of address are multiplexed on PMD<7:0> pins,
* upper 8 bits are on PMA<15:8>
* 00 = Address and data appear on separate pins
*
* bit 10 PMPTTL: PMP Module TTL Input Buffer Select bit
* 1 = PMP module uses TTL input buffers
* 0 = PMP module uses Schmitt Trigger input buffer
*
* bit 9 PTWREN: Write Enable Strobe Port Enable bit
* 1 = PMWR/PMENB port enabled
* 0 = PMWR/PMENB port disabled
*
* bit 8 PTRDEN: Read/Write Strobe Port Enable bit
* 1 = PMRD/PMWR port enabled
* 0 = PMRD/PMWR port disabled
*
* bit 7-6 CSF<1:0>: Chip Select Function bits(4)
* 11 = Reserved
* 10 = PMCS2 and PMCS1 function as Chip Select
* 01 = PMCS2 functions as Chip Select, PMCS1 functions as address bit 14
* 00 = PMCS2 and PMCS1 function as address bits 15 and 14
*
* bit 5 ALP: Address Latch Polarity bit(4)
* 1 = Active-high (PMALL and PMALH)
* 0 = Active-low (PMALL and PMALH)
*
* bit 4 CS2P: Chip Select 1 Polarity bit(4)
* 1 = Active-high (PMCS2)
* 0 = Active-low (PMCS2)
*
* bit 3 CS1P: Chip Select 0 Polarity bit(4)
* 1 = Active-high (PMCS1)
* 0 = Active-low (PMCS1)
*
* bit 2 Reserved: Write ?0?; ignore read
*
* bit 1 WRSP: Write Strobe Polarity bit
* For Slave Modes and Master mode 2 (PMMODE<9:8> = 00,01,10):
* 1 = Write strobe active-high (PMWR)
* 0 = Write strobe active-low (PMWR)
* For Master mode 1 (PMMODE<9:8> = 11):
* 1 = Enable strobe active-high (PMENB)
* 0 = Enable strobe active-low (PMENB)
*
* bit 0 RDSP: Read Strobe Polarity bit
* For Slave modes and Master mode 2 (PMMODE<9:8> = 00,01,10):
* 1 = Read Strobe active-high (PMRD)
* 0 = Read Strobe active-low (PMRD)
* For Master mode 1 (PMMODE<9:8> = 11):
* 1 = Read/write strobe active-high (PMRD/PMWR)
* 0 = Read/write strobe active-low (PMRD/PMWR)
*
* [1] The pmp.h header file is located in the peripheral include directory
* /Microchip/MPLAB\ C32\ Suite/pic32mx/include/peripheral/pmp.h).
*/
void initialize_display(void)
{
unsigned int control, mode, port, interrupt;
/*
* PMCON - Initialization pattern for the PMP control register
*
* The definitions of the positions PMCON_*_POSITION are for the
* PIC32MX340F512 made in
* /Microchip/MPLAB\ C32\ Suite/pic32mx/include/proc/p32mx340f512h.h
* To get a listing, simply run
* $ grep -R PMCON_ /cygdrive/c/Microchip/MPLAB\ C32\ Suite/pic32mx/include/proc/p32mx340f512h.h
*
* The initialization pattern for the PMP control register is composed from
* the following terms:
* 1000000000000000 (=1<<15 = 0x8000), from PMP_ON
* | 0000001100000000 (=3<<8 = 0x0300), from PMP_READ_WRITE_EN
* | 0000000000100000 (=1<<5 = 0x0020), from PMP_LATCH_POL_HI
* | 0000000000000010 (=1<<1 = 0x0002), from PMP_WRITE_POL_HI
* | 0000000000000001 (=1<<0 = 0x0001), from PMP_READ_POL_HI
* --------------------
* = 1000001100100011 (= 0x8323), resulting control pattern
*
* Note: The control setting recommended by Di Lucio [1] is 0x83FB; however,
* this implies that the reserved control bit 2 of the PMCON register is
* set to '1', in contrary to the recommended value of '0' as written in
* the PIC32MX Family Reference Manual, Chapter 13 (DS61128E, page 13-6).
*
* [1] Jasio Di Lucio, "Programming 32-bit microcontrollers in C" (Newnes,
* Amsterdam, 2008).
*/
control = PMP_ON /* 0x8000: (1 << _PMCON_ON_POSITION), with _PMCON_ON_POSITION=0x0000000F=15: Configure PMP enabled */
| PMP_IDLE_CON /* 0x0000: Operate during IDLE (default) */
| PMP_MUX_OFF /* 0x0000: No 16-bit multilplexing (default) */
| PMP_ST /* 0x0000: PMP Input buffer type as Schmidt Trigger inputs (default) */
| PMP_READ_WRITE_EN /* 0x0300: (3 << _PMCON_PTRDEN_POSITION), with _PMCON_PTRDEN_POSITION=0x00000008=8: Configure RD, RD/WR strobe = ON; WR, WR/ENB strobe = ON */
| PMP_LATCH_POL_HI /* 0x0020: (1 << _PMCON_ALP_POSITION), with _PMCON_ALP_POSITION = 0x00000005 */
| PMP_WRITE_POL_HI /* 0x0002: (1 << _PMCON_WRSP_POSITION), with _PMCON_WRSP_POSITION = 0x00000001 */
| PMP_READ_POL_HI; /* 0x0001: (1 << _PMCON_RDSP_POSITION), with _PMCON_RDSP_POSITION = 0x00000000 */
/* 0x8323: Result after bitwise OR on the above patterns */
/*
* PMMODE - Initialization pattern for the PMP mode
*
* The initialization pattern for the PMP mode register is composed from
* the following terms:
* 0000000000000000 (0x0000), from PMP_IRQ_OFF (default)
* | 0000000000000000 (0x0000), from PMP_AUTO_ADDR_OFF (default)
* | 0000000000000000 (0x0000), from PMP_DATA_BUS_8 (default)
* | 0000001100000000 (=3<<8 = 0x0300), from PMP_MODE_MASTER1
* | 0000000011000000 (=3<<6 = 0x00c0), from PMP_WAIT_BEG_4
* | 0000000000111100 (=15<<2 = 0x003c), from PMP_WAIT_MID_15
* | 0000000000000011 (=3<<0 = 0x0003), from PMP_WAIT_END_4
* --------------------
* = 0000001111111111 (=0x03FF), resulting mode pattern
*/
mode = PMP_IRQ_OFF /* 0x0000: PMP interrupt mode OFF */
| PMP_AUTO_ADDR_OFF /* 0x0000: Auto Increment/Decrement OFF */
| PMP_DATA_BUS_8 /* 0x0000: Configure 8-bit data mode (default) */
| PMP_MODE_MASTER1 /* 0x0300: (3 << _PMMODE_MODE_POSITION), with _PMMODE_MODE_POSITION = 0x00000008; Configure MASTER mode 1 */
| PMP_WAIT_BEG_4 /* 0x00c0: (3 << _PMMODE_WAITB_POSITION), with _PMMODE_WAITB_POSITION = 0x00000006; Beginning phase wait state as "4 Tpb WAIT" */
| PMP_WAIT_MID_15 /* 0x003c: (15 << _PMMODE_WAITM_POSITION), with _PMMODE_WAITM_POSITION = 0x00000002; Middle phase wait state as "3 Tpb WAIT" */
| PMP_WAIT_END_4; /* 0x0003: (3 << _PMMODE_WAITE_POSITION), with _PMMODE_WAITE_POSITION = 0x00000000; End phase wait state as "4 Tpb WAIT" */
/* 0x03FF: Result after bitwise OR on the above patterns */
port = PMP_PEN_0; /* 0x0001: PMA0 enabled as the address line for register selection */
interrupt=PMP_INT_OFF; /* 0x0000: No interrupts used here */
/*
* Employ the mPMPOpen(control, mode, port, interrupt) macro for the actual
* assignment of the control, mode and port patterns to the PMCON (control),
* PMMODE (mode) and PMAEN (port) registers. Also configure any interrupts
* (which, however, are not used here).
* The mPMPOpen(control, mode, port, interrupt) macro is simply defined as
* #define mPMPOpen(control, mode, port, interrupt)\
* (\
* PMCON = (control),\
* PMMODE = (mode),\
* PMAEN = (port),\
* mPMPClearIntFlag(),\
* mPMPSetIntPriority(((interrupt) & 7)),\
* mPMPSetIntSubPriority((((interrupt)>> 4) & 3)),\
* mPMPIntEnable(((interrupt) >> 15))\
* )
*/
mPMPOpen(control, mode, port, interrupt);
delayms(32); /* wait for >30ms */
/*
* Initiate the DOGM163 display with 8-bit initialization sequence. For details,
* see the Electronic Assembly DOG-M data sheet "DOG Series 3.3V", Issue 1.2010,
* page 7, "Examples for initialisation", available at
* http://www.lcd-module.de/eng/pdf/doma/dog-me.pdf.
*
* Notice that the PMPSetAddress(unsigned int addrs) function (defined as an
* inline function directly in the pmp.h header file) simply consists of the
* two lines
*
* while(mIsPMPBusy());
* PMADDR = addrs;
*
* while PMPMasterWrite(unsigned short value) consists of
*
* while(mIsPMPBusy());
* PMDIN = value;
*/
PMPSetAddress(LCDCMD);/* Select command register */
PMPMasterWrite(DOGM_FUNCTION_SET /* Function Set: 00100000 (= 0x20) */
|DOGM_EIGHT_BIT_INTERFACE /* 8 bit data: |00010000 (= 0x10) */
|DOGM_NUM_LINE_IS_TWO /* 2 lines: |00001000 (= 0x08) */
|DOGM_SINGLE_HEIGHT_FONT /* Single height: |00000000 (= 0x00) */
|DOGM_INSTRUCTION_TABLE_ONE);/* Select Table 1:|00000001 (= 0x01) */
delayms(32); /* >30 us */ /* Result pattern: 00111001 (= 0x39) */
PMPMasterWrite(DOGM_BIAS_SET /* Bias set: 00010100 (= 0x14) */
|DOGM_ONE_FIFTH_BIAS /* 1/5:th bias: |00000000 (= 0x00) */
|DOGM_THREELINE_BIAS); /* 3 lines: |00000001 (= 0x01) */
delayms(32); /* >30 us */ /* Result pattern: 00010101 (= 0x15) */
PMPMasterWrite(0x55); /* Power Control Set: Booster on, contrast C5, set C4 */
delayms(32); /* > 30 us */
PMPMasterWrite(0x6e); /* Follower Control Set: Set voltage follower and gain */
delayms(32); /* > 30 us */
PMPMasterWrite(0x72); /* Contrast Set: Set contrast C3, C2, C1 */
delayms(32); /* > 30 us */
PMPMasterWrite(0x38); /* Function Set: Switch back to instruction table 0 */
delayms(32); /* > 30 us */
PMPMasterWrite(DOGM_DISPLAY_ON /* Display Settings: */
|DOGM_ENTIRE_DISPLAY_ON /* Set display ON, ... */
|DOGM_CURSOR_OFF /* ... cursor ON, ... */
|DOGM_CURSOR_POSITION_OFF); /* ... cursor blink ON. */
delayms(32); /* > 30 us */
PMPMasterWrite(DOGM_CLEAR__DISPLAY); /* Clear display, cursor to home */
delayms(32); /* > 30 us */
PMPMasterWrite(DOGM_ENTRY_MODE_SET
|DOGM_CURSOR_MOVE_DIR_FORWARD
|DOGM_CURSOR_SHIFT_DIR_DOWN);
delayms(32); /* > 30 us */
return;
}
/*
* The delay: Needed or not?
*/
#define READ_DISPLAY_REGISTER_DELAY (7)
char read_display_register(int addr)
{
PMPSetAddress(addr); /* Select register */
/* delayms(READ_DISPLAY_REGISTER_DELAY); */
mPMPMasterReadByte(); /* Initiate read sequence */
/* delayms(READ_DISPLAY_REGISTER_DELAY); */
return(mPMPMasterReadByte()); /* Read actual data */
}
#undef READ_DISPLAY_REGISTER_DELAY
/*
* Read the address counter of the LCD using R/W=1(high), RS=0(low), implying
* a 0x02 address mask. The address counter is returned in the 7 lowest bits
* of the ACx register, hence the masking by 0x7f=01111111.
*/
int read_current_address_counter(void)
{
return(read_display_register(0x02)&0x3F);
}
/*
* The display_is_busy() routine obtains the status of the display and returns
* 0 if the display is ready to receive commands; otherwise a non-zero value is
* returned. Essentially, the routine reads the LCDCMD register via the PMP and
* masks out the BF bit using R/W=1(high), RS=0(low); see "Read Busy Flag and
* Address" in DOGM data sheet, available at
* http://www.lcd-module.com/eng/pdf/doma/dog-me.pdf
*/
char display_is_busy(void)
{
return (read_display_register(LCDCMD)&0x80);
}
void write_to_display_register(int addr, char c)
{
while (display_is_busy());
while (PMMODEbits.BUSY);
PMPSetAddress(addr);
PMPMasterWrite(c);
return;
}
void write_character_to_display(char c)
{
write_to_display_register(LCDDATA,c);
col++;
if (col>DOGM163_NUM_COLS) {
col=1;
row++;
if (row>DOGM163_NUM_ROWS) row=1;
}
return;
}
void send_command_to_display(char c) /*cmdLCD*/
{
write_to_display_register(LCDCMD,c);
return;
}
void setLCDG(char a)
{
send_command_to_display((a&0x3F)|0x40);
return;
}
void setLCDC(char a) {
send_command_to_display((a&0x7F)|0x80);
return;
}
void clear_display(void)
{
send_command_to_display(DOGM_CLEAR__DISPLAY);
row=1;
col=1;
delayms(1);
return;
}
void move_to_position_on_display(int rowpos, int colpos)
{
int k;
row=rowpos;
col=colpos;
if (row>DOGM163_NUM_ROWS) row=1;
if (col>DOGM163_NUM_COLS) col=1;
if (row==1) {
k=col-1; /* Avoid multiplication if possible */
} else {
k=col-1+(row-1)*DOGM163_NUM_COLS;
}
send_command_to_display(0x80|k); /* Set address counter to identified position */
return;
}
/*
* Move to upper left corner of display, but do not wipe out any characters
* already written to the display (in contrary to the clear_display() routine).
*/
void move_to_beginning_of_display(void)
{
send_command_to_display(DOGM_RETURN_HOME);
row=1;
col=1;
return;
}
/*
* Move cursor to the beginning of the current line.
*/
void move_to_beginning_of_line(void)
{
move_to_position_on_display(row, 1); /* Same row, first column */
return;
}
/*
* Move cursor to the beginning of the next line.
*/
void advance_to_next_line(void)
{
move_to_position_on_display(row+1, 1); /* Next row, first column */
return;
}
void write_string_to_display(char *s)
{
char k;
while(*s!='\0') { /* Until termination character of string */
switch (*s) {
case '\t': /* Print out DOGM_TAB_LENGTH tab characters */
for (k=0;k