I'm working on an LCD controller in a Microblaze soft CPU I've embedded in an FPGA. To connect to the top level Verilog file's outputs, I've used a single 8bit GPO.
In my C, I've used placeholder variables for each bit I'm controlling, for example:
LcdDataBus = (cmd & 0xF0); //Send higher nibble
LCD_RS = 0;
LCD_RW = 0;
LCD_EN = 1;
If I were programming a PIC, I could simply define each as a pin on the uC, i.e. #define LCD_RS PORTA,0
.
However, I believe I can only access the port through a function
data = XIOModule_Initialize(&gpo, XPAR_IOMODULE_0_DEVICE_ID);
data = XIOModule_Start(&gpo);
Where data
is some variable.
Is there a way I could #define
a single bit in data
, so LCD_RS = bit0, LCD_RW = bit1, LCD_E = bit3, and my LCD data bus could be the next four bits?
As you want a very precise defined way of bit control, the safest way is to use read-modify-write.
volatile uint32_t *reg_address;
uint32_t data;
data = *reg_address;
data |= (1<<LCD_RS_BIT_POS); // Set bit
// data &= ~(1<<LCD_RS_BIT_POS); // Clear bit
*reg_address = data;
Alternative is to add bit-set/bit clear address locations to you LCD port register map (This is Verilog code):
if (cpu_select && cpu_write)
case (cpu_address[4:2])
3'h0 : lcd_reg <= cpu_wdata[7:0]; // Direct write
3'h1 : lcd_reg <= lcd_reg | cpu_wdata[7:0]; // set bit(s) write
3'h2 : lcd_reg <= lcd_reg & ~cpu_wdata[7:0]; // Clear bit(s) write
....
endcase
You asked for setting more bits.
A basic approach is to clear all the bits you want to change, then set them again with the desired values.
For example: You have a 4 bit value (bits 4,5,6,7) a R/W bit (2) plus an address bit (0). You turn those into a mask: 0b11110101.
// Write to LCD
data = *reg_address;
data &= ~0xF5; // Clear all the bits
if (address==1)
data |= (data_nibble<<4) | (1<<LCD_WRT_BIT_POS) |(1<<LCD_ADR_BIT_POS) ;
else // Address 0
data |= (data_nibble<<4) | (1<<LCD_WRT_BIT_POS);
*reg_address = data;
But....
In your case you probably only need to control the 'E' signal separately. And you know that it should start low.The whole procedure becomes simpler:
uint32_t data;
// Set the data, R/Wnot and RS all at the same time
if (write_data)
data = nibble_data<<4; // R/Wn=0, RS=0. E=0
else // Write command
data = nibble_data<<4 | (1<<RS_BIT_POS); // R/Wn=0, RS=1, E=0
*reg_address = data;
// Pulse E high and low:
*reg_address = data | (1<<E_BIT_POS); // E high
some_kind_of_wait_routine(); // Wait a while
*reg_address = data; // E low
some_kind_of_wait_routine(); // Wait a while to guarantee minimum low time!
For a read you do not need to set the data but you must set the Read line high. However the LCD display can be operated without reading.