0
votes

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?

2
Bitwise shifts and OR operations is one way to go. And don't forget to use only unsigned data types.Some programmer dude
You could do it in a structHenricus V.
If you have written the LCD Verilog you could add the bit-set and bit-clear operations in there.Oldfart

2 Answers

0
votes

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.

0
votes

You can create a bitfields within a struct:

typedef struct {
  Uint32 LCD_RS : 1, /* 1 is the size in bit */
         LCD_RW : 1,
         LCD_E  : 1,
         bus    : 4
                : 15;
} data;