Our Raspberry Pi friends will know the Building a Low Power Compact LCD Display project that shabaz published a while ago. He designed a £5 LCD module and the source code for Raspberry Pi.
I have ported that code to the Texas Instruments Hercules family. The hardware is exactly the same as in the original article. Take care to read shabaz' blog first. It's a good read and this blog builds upon the information. |
Porting from Pi to Hercules
The Pi code is linux based. It uses the linux i2c file device to talk to the LCD driver.
Our Hercules isn't a linux controller. Our work will be to port from the file type i2c access to a hardware driver based API.
The API is documented in the HALCoGen configuration program.
That help set also has an example that we can use to understand how a typical i2c conversation looks like: example_i2cMaster_TX_RX.
The first part of that example is a Master Send scenario with a loop. Exactly the same as what shabaz uses in his Pi test.
That means we don't have to find out what API functions to call when. We can just replace the each linux-based set of calls in the Pi code with the ones from the example.
I have only ported lcd3-test.c.
Shabaz uses i2c abstraction utility functions in his design. HALCoGen already has an abstraction API, so I'm using that.
You will see that the changes are very few.
header declarations:
Pi
#include <stdio.h> #include "i2cfunc.h"
Hercules
#include <stdio.h> #include "i2c.h"
constants and variables:
Pi
// we will use I2C2 which is enumerated as 1 on the BBB #define I2CBUS 1 // set to 1 to print out the intermediate calculations #define DBG_PRINT 0 // set to 1 to use the example values in the datasheet // rather than the values retrieved via i2c #define ALG_TEST 0 #define LCD_ADDR 0x38 // Four possible values for DEVICE_ADDRESS // R1 R2 R3 R4 DEVICE_ADDRESS // Fit FIT 0x60 // Fit Fit 0x61 // Fit Fit 0x62 // Fit Fit 0x63 // Commands (these can be 'continued' by setting the MSB #define DEVICE_ADDR 0x60 #define BLINK_OFF 0x70 #define BANK_CMD 0x78 // BANK_CMD bit values #define WR_BANK_A 0x00 #define WR_BANK_B 0x02 #define DISP_BANK_A 0x00 #define DISP_BANK_B 0x01 typedef struct char_prog_s { unsigned char byte0; unsigned char byte1; unsigned char byte2; } char_prog_t; typedef struct ascii_s { char_prog_t char0; char_prog_t char1; char_prog_t char2; } ascii_t; const char bit_table[]= { 0x38, 0x00, 0x86, 0x00, 0xbe, 0x00, 0x86, 0x00, 0x38, /* 0 */ 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x02, 0x00, 0x08, /* 1 */ 0x30, 0x00, 0x46, 0x00, 0x76, 0x00, 0x46, 0x00, 0x30, /* 2 */ 0x18, 0x00, 0x46, 0x00, 0x5e, 0x00, 0x46, 0x00, 0x18, /* 3 */ 0x08, 0x00, 0xc2, 0x00, 0xca, 0x00, 0xc2, 0x00, 0x08, /* 4 */ 0x18, 0x00, 0xc4, 0x00, 0xdc, 0x00, 0xc4, 0x00, 0x18, /* 5 */ 0x38, 0x00, 0xc4, 0x00, 0xfc, 0x00, 0xc4, 0x00, 0x38, /* 6 */ 0x08, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x08, /* 7 */ 0x38, 0x00, 0xc6, 0x00, 0xfe, 0x00, 0xc6, 0x00, 0x38, /* 8 */ 0x18, 0x00, 0xc6, 0x00, 0xde, 0x00, 0xc6, 0x00, 0x18 /* 9 */ }; unsigned char buf[10]; int handle;
Hercules
// set to 1 to use the example values in the datasheet // rather than the values retrieved via i2c #define ALG_TEST 0 #define LCD_ADDR 0x38 // Four possible values for DEVICE_ADDRESS // R1 R2 R3 R4 DEVICE_ADDRESS // Fit FIT 0x60 // Fit Fit 0x61 // Fit Fit 0x62 // Fit Fit 0x63 // Commands (these can be 'continued' by setting the MSB // jc20161120 R1 and R4 mounted on my board #define DEVICE_ADDR 0x62 #define BLINK_OFF 0x70 #define BANK_CMD 0x78 // BANK_CMD bit values #define WR_BANK_A 0x00 #define WR_BANK_B 0x02 #define DISP_BANK_A 0x00 #define DISP_BANK_B 0x01 typedef struct char_prog_s { unsigned char byte0; unsigned char byte1; unsigned char byte2; } char_prog_t; typedef struct ascii_s { char_prog_t char0; char_prog_t char1; char_prog_t char2; } ascii_t; const char bit_table[] = { 0x38, 0x00, 0x86, 0x00, 0xbe, 0x00, 0x86, 0x00, 0x38, //0 0x08, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x02, 0x00, 0x08, //1 0x30, 0x00, 0x46, 0x00, 0x76, 0x00, 0x46, 0x00, 0x30, //2 0x18, 0x00, 0x46, 0x00, 0x5e, 0x00, 0x46, 0x00, 0x18, //3 0x08, 0x00, 0xc2, 0x00, 0xca, 0x00, 0xc2, 0x00, 0x08, //4 0x18, 0x00, 0xc4, 0x00, 0xdc, 0x00, 0xc4, 0x00, 0x18, //5 0x38, 0x00, 0xc4, 0x00, 0xfc, 0x00, 0xc4, 0x00, 0x38, //6 0x08, 0x00, 0x06, 0x00, 0x0e, 0x00, 0x06, 0x00, 0x08, //7 0x38, 0x00, 0xc6, 0x00, 0xfe, 0x00, 0xc6, 0x00, 0x38, //8 0x18, 0x00, 0xc6, 0x00, 0xde, 0x00, 0xc6, 0x00, 0x18 //9 }; unsigned char buf[10];
The char_prog() function is not changed at all, so I'm skipping that.
print_line():
Pi
void print_line(char* line) { // ... buf[5]=byte0; buf[6]=byte1; buf[7]=byte2; i2c_write(handle, buf, 8); }
Hercules
void print_line(char* line) { // ... int delay; // ... buf[5] = byte0; buf[6] = byte1; buf[7] = byte2; /* Configure Data count */ i2cSetCount(i2cREG1, 8); // buffer values /* Set mode as Master */ i2cSetMode(i2cREG1, I2C_MASTER); /* Set Stop after programmed Count */ i2cSetStop(i2cREG1); /* Transmit Start Condition */ i2cSetStart(i2cREG1); /* Tranmit DATA_COUNT number of data in Polling mode */ i2cSend(i2cREG1, 8, buf); /* Wait until Bus Busy is cleared */ while (i2cIsBusBusy(i2cREG1) == true) ; /* Wait until Stop is detected */ while (i2cIsStopDetected(i2cREG1) == 0) ; /* Clear the Stop condition */ i2cClearSCD(i2cREG1); for(delay=0;delay<1000000;delay++); // todo: I will have to check in the specs how long I have to wait before I can submit the next conversation. }
The part that shabaz has put in the main() function, I've extracted into a function called lcd3_test().
This because the main() in a non-linux environment has a different nature than bare metal, and I wanted to keep the flow as close to shabaz' as possible.
Pi
int main(void) { int i; double v; unsigned char text[4]; char_prog_t *progval; unsigned char mode, device, bank, blinkmode; mode=0xc9; // Set static mode, display enabled, continuation enabled device=DEVICE_ADDR | 0x80; // Select the device, continuation enabled bank=BANK_CMD | 0x80 | WR_BANK_A | DISP_BANK_A; blinkmode=BLINK_OFF | 0x80; buf[0]=mode; buf[1]=device; buf[2]=blinkmode; buf[3]=bank; buf[4]=0x00; // pointer handle=i2c_open(I2CBUS, 0x38); buf[5]=0xf1; // data buf[6]=0x80; // data buf[7]=0x02; // data progval=char_prog(0, '5'); buf[5]=progval->byte0; buf[6]=progval->byte1; buf[7]=progval->byte2; print_line(" . ."); for (v=0.0; v<99.9; v=v+0.1) { sprintf(text, "%4.1f", v); print_line(text); delay_ms(40); } delay_ms(500); print_line(" "); i2c_close(handle); return(0); }
Hercules
int lcd3_test(void) { double v; char text[4]; char_prog_t *progval; unsigned char mode, device, bank, blinkmode; i2cInit(); mode = 0xc9; // Set static mode, display enabled, continuation enabled device = DEVICE_ADDR | 0x80; // Select the device, continuation enabled bank = BANK_CMD | 0x80 | WR_BANK_A | DISP_BANK_A; blinkmode = BLINK_OFF | 0x80; buf[0] = mode; buf[1] = device; buf[2] = blinkmode; buf[3] = bank; buf[4] = 0x00; // pointer buf[5] = 0xf1; // data buf[6] = 0x80; // data buf[7] = 0x02; // data progval = char_prog(0, '5'); buf[5] = progval->byte0; buf[6] = progval->byte1; buf[7] = progval->byte2; /* Configure address of Slave to talk to */ i2cSetSlaveAdd(i2cREG1, LCD_ADDR); /* Set direction to Transmitter */ /* Note: Optional - It is done in Init */ i2cSetDirection(i2cREG1, I2C_TRANSMITTER); print_line(" . ."); for (v = 0.0; v < 99.9; v = v + 0.1) { sprintf(text, "%4.1f", v); print_line(text); } print_line(" "); return (0); }
and for completeness, the Hercules main() function:
int main(void) { lcd3_test(); while(1) { } return 0; }
Configure I2C
True to the Hercules way of working, we set up the i2c driver in HALCoGen.
- enable the driver
- set multiplexing to enable i2c functionality
- set the parameters
Connections
We need 4 connections: ground, 3V3, SDA and SCL.
The location of these pins on the LaunchPad depends on witch one you have. Check the schematic file for the right pins.
I'm using an RM46 here.
For that board, all available pins are on Proto Board Header 11
signal | LCD board | LaunchXL2-RM46 |
---|---|---|
GND | 1 | J11-2 |
I2C SDA | 2 | J11-9 |
I2C SCL | 3 | J11-8 |
3V3 | 4 and 5 | J11-3 |
If you prefer, you can also use BoosterPack position 1 for the connections:
CCS project file is attached to this blog. It contains all sources and the HALCoGen configuration for the RM46.
This is an easy-to-replicate exercise. Both for hardware and software.
Spend £5 and check shabaz' instructions for the components, PCB design and build steps.
Then give it a go on your Pi and Hercules.
Have fun!
Top Comments