0

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?

nick_g
  • 489
  • 1
  • 7
  • 15

2 Answers2

0

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;
Umaiki
  • 354
  • 2
  • 14
  • 1
    I guess you mean `:` instead of `=`? – PhilMasteG Jun 20 '18 at 12:47
  • 6
    Be careful with [relying on bit field layout](https://wiki.sei.cmu.edu/confluence/display/c/EXP11-C.+Do+not+make+assumptions+regarding+the+layout+of+structures+with+bit-fields). – rustyx Jun 20 '18 at 12:48
  • That does not control the way the bit is written. We are talking hardware registers here. Also the language allows the bit mapping to be ignored. – Oldfart Jun 20 '18 at 12:49
  • 1
    Bitfields can be made to work with a specific compiler and a specific machine. Once you change one of these parameters, nothing is guaranteed any more. After you have experienced the porting effort of such a construct onto another platform, you will very probably be convinced that bitfields are not a good idea to access hardware directly. – tofro Jun 21 '18 at 19:37
0

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.

Oldfart
  • 6,104
  • 2
  • 13
  • 15