help me understand this AVR C code

this is a working code that is splitting the 8 bit data on two different ports and I am not understanding how it is working. if we AND PORTB to ~0x3F which is 0xC0 , it will make bits DB0-DB5 all zeros so how is the PORTB and PORTC assignments working ?

/*-------------------------------------------------
DATA:
    DB0 DB1 DB2 DB3 DB4 DB5 DB6 DB7 
    
    PB0 PB1 PB2   PB3 PB4 PB5 PC6 PC7

CONTROL:
    RES  RD   WR  RS
    PC2  PC3  PB4 PB5
    
---------------------------------------------------*/
 #define DMSK               0x3F


void write_8(unsigned char c)
{
    PORTB = (PORTB & ~DMSK)|(c & DMSK); 
    PORTC = (PORTC & DMSK)|(c & ~DMSK);
      .
    .
}

void TFT_setup(void)
{
    DDRB |= DMSK;               
    DDRC |= ~DMSK;          
    DDRC |= 0x3C;              
        .
        .
}

This tutorial on the Playground might help you figure that code out

http://playground.arduino.cc/Code/BitMath

Just break each part of the operation down, and use binary values. The playground article will explain the operations. Then it is just a matter of working them out. Tough to get thinking this way, but practice will make it easier.

	PORTB = (PORTB & ~DMSK)|(c & DMSK); 
	PORTC = (PORTC & DMSK)|(c & ~DMSK);

The mask is 0x3F which has the lower 6 bits set. c & DMSK gives a byte with the lower 6 bits from c and the upper 2 bits set to zero. PORTB & ~DMSK gives a byte with the upper 2 bits from PORTB and the lower 6 bits set to zero. Or-ing these two bytes gives a byte with the upper 2 bits from PORTB and the lower 6 bits from c. Similar analysis can be applied to the PORTC assignment.

See comments in the code. What AVR are you going to run this on as it won't work on the UNO as is.

/*-------------------------------------------------
  DATA:
  DB0 DB1 DB2 DB3 DB4 DB5 DB6 DB7 
  
  PB0 PB1 PB2  PB3 PB4 PB5 PC6 PC7
  
  CONTROL:
  RES  RD   WR  RS
  PC2  PC3  PB4 PB5
  
---------------------------------------------------*/
#define DMSK  0x3F // = Binary 00111111


void write_8(unsigned char c)
{
    PORTB = (PORTB & ~DMSK)|(c & DMSK); // Read PORTB and mask to keep upper 2 bits, Mask c to keep lower 6 bits, OR both together and write back to PORTB
    PORTC = (PORTC & DMSK)|(c & ~DMSK); // Read PORTC and mask to keep lower 6 bits, mask c to keep upper 2 bits, OR both and write back to PORTC
}

void TFT_setup(void)
{
    DDRB |= DMSK; // Set lower 6 bits of PORTB as output (upper 2 bits remain the same as the value is OR'ed)
    DDRC |= ~DMSK; // Set upper 6 bits of PORTC as output
    DDRC |= 0x3C; // Set bits 2-5 of PORTC as output
}

Riva: See comments in the code. What AVR are you going to run this on as it won't work on the UNO as is.

/*-------------------------------------------------
  DATA:
  DB0 DB1 DB2 DB3 DB4 DB5 DB6 DB7 
  
  PB0 PB1 PB2  PB3 PB4 PB5 PC6 PC7
  
  CONTROL:
  RES  RD   WR  RS
  PC2  PC3  PB4 PB5
  
---------------------------------------------------*/
#define DMSK  0x3F // = Binary 00111111

void write_8(unsigned char c) { PORTB = (PORTB & ~DMSK)|(c & DMSK); // Read PORTB and mask to keep upper 2 bits, Mask c to keep lower 6 bits, OR both together and write back to PORTB PORTC = (PORTC & DMSK)|(c & ~DMSK); // Read PORTC and mask to keep lower 6 bits, mask c to keep upper 2 bits, OR both and write back to PORTC }

void TFT_setup(void) { DDRB |= DMSK; // Set lower 6 bits of PORTB as output (upper 2 bits remain the same as the value is OR'ed) DDRC |= ~DMSK; // Set upper 6 bits of PORTC as output DDRC |= 0x3C; // Set bits 2-5 of PORTC as output }

oh I see it now ,, PORT B bits 0-5 are made zero but when OR'ed with data gives the pins correct values. I am running this code on atmega1284 .

regards

so if i change the pin mapping to follows :

DATA:
    DB0   DB1     DB2    DB3    DB4    DB5     DB6   DB7 
    PB0    PB1     PB2    PC3     PC4     PC5     PC6    PC7
CONTROL:
    RES   RD     WR      RS
    PC2   PB5    PD3    PD2

would the following assignments be correct ?

void write_8(unsigned char c)
{
 	PORTB = (PORTB & 0xF8)|(c & 07);  // Read PORTB and mask to keep upper 5 bits, Mask c to keep lower 3 bits,  OR both together and write back to PORTB
 	PORTC = (PORTC & 07)|(c & F8);    // Read PORTC and mask to keep lower 3 bits, mask c to keep upper 5 bits,  OR both and write back to PORTC
}

void TFT_setup(void)
{
DDRB |= ((1<<PORTB0)|(1<<PORTB1)|(1<<PORTB2)|(1<<PORTB5)) 
DDRC |= ((1<<PORTC2)|(1<<PORTC3)|(1<<PORTC4)|(1<<PORTC5)|(1<<PORTC6)|(1<<PORTC7))
DDRD |= ((1<<PORTD2)|(1<<PORTD3))
}

please note I am using one DDRC command to set the data and control bits as output and not two DDRC commands as in original post.

I obviously have no idea of your hardware layout/limitations but if your designing it yourself then life would be so much easier if you could dedicate all 8 pins on a port to the 8 bit data bus. It should also speed up display refresh quite a bit as less instructions needed to just write a value to a single port instead or Reading, AND'ing, OR'ing & writing to two separate ports.

my hardware requirment does not leave me any contigious sets of pins to do that.
can someone tell me if the following two are equivalent ? :

DDRB |= 0x27;
DDRC |= 0xFC;
DDRD |= 0x0C;

DDRB |= ((1<<PORTB0)|(1<<PORTB1)|(1<<PORTB2)|(1<<PORTB5))
DDRC |= ((1<<PORTC2)|(1<<PORTC3)|(1<<PORTC4)|(1<<PORTC5)|(1<<PORTC6)|(1<<PORTC7))
DDRD |= ((1<<PORTD2)|(1<<PORTD3))

It is easy to determine equivalence. Just do something like this and look at the Serial monitor

#include <Arduino.h>
#include <avr/io.h>

void setup()
{
  Serial.begin(9600);
  uint8_t tmp = 0;
  tmp |= ((1<<PORTB0)|(1<<PORTB1)|(1<<PORTB2)|(1<<PORTB5));
  Serial.println(tmp, HEX);

  tmp = 0;
  tmp |= ((1<<PORTC2)|(1<<PORTC3)|(1<<PORTC4)|(1<<PORTC5)|(1<<PORTC6)|(1<<PORTC7));
  Serial.println(tmp, HEX);

  tmp = 0;
  tmp |= ((1<<PORTD2)|(1<<PORTD3));
  Serial.println(tmp, HEX);
}

void loop()
{
}

produces:

27
FC
C

However, the original code is more readable. It is much clearer what the line of code intends.