AVR Atmega168 I2C LCD does not want to initialize - c

I'm using I2C converter to send data to my lcd.
The converter is based on PCF85741, and the lcd is Hitachi hd44780.
Port mapping between PCF85741 and lcd is as follows:
P0 -> RS
P1 -> RW
P2 -> E
P3 -> ?
P4 -> D4
P5 -> D5
P6 -> D6
P7 -> D7
The documentation says that the default address of my slave is 0x20, but with RW bit I need to send 0x40.
Here is my code:
void twi_start()
{
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTA);
while (!(TWCR & (1 << TWINT)));
}
void twi_stop()
{
TWCR = (1 << TWINT) | (1 << TWEN) | (1 << TWSTO);
while (!(TWCR & (1 << TWSTO)));
}
void twi_write(uint8_t byte)
{
TWDR = byte;
TWCR = (1 << TWINT) | (1 << TWEN);
while (!(TWCR & (1 << TWINT)));
}
void twi_write_byte(uint8_t byte)
{
uint8_t SLAVE_ADDRESS = 0x40;
twi_start();
twi_write(SLAVE_ADDRESS);
twi_write(byte);
twi_stop();
}
Lcd init
void lcd_init2()
{
for (int i = 0; i < 3; i++) {
twi_write_byte(0x03);
_delay_ms(20);
}
twi_write_byte(0x02);
_delay_ms(20);
//4 bit mode
twi_write_byte(0x24); // D5 -> 1, E -> 1
_delay_ms(10);
twi_write_byte(0x20); // D5 -> 1, E -> 0
_delay_ms(10);
//2 lines
twi_write_byte(0x24); // D5 -> 1, E -> 1
_delay_ms(10);
twi_write_byte(0x20); // D5 -> 1, E -> 0 first nibble
_delay_ms(10);
twi_write_byte(0x84); // D7 -> 1, E -> 1
_delay_ms(10);
twi_write_byte(0x80); // D7 -> 1, E -> 0 second nibble
_delay_ms(10);
}
After this code, the lcd should be in 4bit mode, with 2 lines, but it isn't
Nothing changes on lcd.

1) Please clarify what the I2C-to-parallel IC you have? I cannot find what is PCF85741, I see datasheets only for PCF8574 and PCF8574A.
In the first case, the slave address will be (including r/w bit) 0x40...0x4F, in the second - 0x70...0x7F.
Speaking from my experience the usual I2C circuit which comes with those displays has PCF8574A on it (i.e. address is 0x7*). And pin 3 there, by the way, is used to control the backlight.
2) Make sure what lower bits of the address you have? Are inputs A0 A1 A2 pulled up or tied to ground?
Again, from my experience, those boards usually have pull-ups to +5, and solderable jumpers on the board, to short those to ground. By default, the jumpers are not soldered, therefore A0 A1 A2 has a high logical level, thus the I2C address of the device is 0x7E (writing)/0x7F (reading). If you have PCF8574T on board, then the address will be 0x4E/0x4F
You can easily detect has the IC answered or not, by checking TWS bits in TWSR register (TWSR & TW_STATUS_MASK) should be equal to TW_MT_SLA_ACK (0x18) after the address is transmitted, or TW_MT_DATA_ACK (0x28) after the data byte is transmitted. (See the datasheet section 19.8.1 Master Transmitter Mode pages 186-188)
Or, more simply, if you have P3 of the PCF8574 connected to the backlight, you can try output 0x08 / 0x00 to see if the backlight is turning on and off.
3) For the initialization sequence look at Figure 24 4-Bit Interface on page 46 of HD44780 datasheet
Note bits DB5 and DB4 are high. Also, be careful, since you're only writing to the display controller, the bit R/W (i.e. the bit 1 of the output) should always be zero (note you're sending wi_write_byte(0x03); and then twi_write_byte(0x02); with the bit 1 is set high).
The example may be as follows:
void send4bits(uint8_t fourbits, bool is_cmd) {
uint8_t d = (fourbits << 4) | 0b1000;
if (!is_cmd) d |= 1;
twi_write_byte(d | 0b100); // E high
twi_write_byte(d); // E low
}
void sendcmd(uint8_t cmd) {
send4bits(cmd >> 4, true);
send4bits(cmd & 0xF, true);
}
void senddata(uint8_t cmd) {
send4bits(cmd >> 4, false);
send4bits(cmd & 0xF, false);
}
// initialization sequence
send4bits(0b0011, true);
_delay_ms(5);
send4bits(0b0011, true);
_delay_ms(1);
send4bits(0b0011, true);
// since I2C is slow enough the required 100us pause already happened here
send4bits(0b0010, true);
sendcmd(0b00101000);
sendcmd(0b00001000);
sendcmd(0b00000001);
delay_ms(2);
sendcmd(0b00000110);
sendcmd(0b00001110);
// Initialization is done
sendcmd(0x80); // Set cursor at the beginning
for (uint8_t i = 'A' ; i <= 'Z' ; i++) {
senddata(i); // Send some random data
}

Related

Arduino bitwise runninglight with a single delay

I have the assignment to create a running light on my Arduino with the following requirements/constraints
Requirements
Implement a socalled “runninglight” with 3 leds.
The on time of each led is the same, being 0.5 sec.
After the last Led is lit, the first starts again.
Constraints
The leds are connected to pin 8,9 and 10 of the Arduino. (don’t forget the resistors)
The running led must be implemented in the loop of the Arduino
The setup() function may contain any Arduino function/macro.
Only the Arduino function delay() is allowed in the loop.
No use of any other Arduino function/macro etc. in the loop.
No use of for/while in the loop() function
No use of your own defined functions
Your loop function must contain not more then 10 statements.
So in the loop you may only use
o Delay function
o Your variables
o The following characters/digits: * ( ) & ~ | ; = < > ! + - 0 1 2 3 4 5 6 7 8 9
I have tried the following code
#include <Arduino.h>
//mask for setting correct bits in register to setup leds to 1
#define DDBMASK 0x07
//masks for setting correct bits in register for led control to 1
#define LEDMASK 0x01
byte *ddBPtr; //point to ddB register (data direction)
byte *portBPtr; //point to portB resiger (data register)
void setup() {
//setup data direction register to set pins to output
ddBPtr = (byte *) 0x24;
*ddBPtr |= DDBMASK; // 0b0000 0111
//assign pointer to right register
portBPtr = (byte *) 0x25;
}
void loop() {
//use data register (portB) for controlling leds
*portBPtr ^= LEDMASK;
delay(500);
*portBPtr ^= LEDMASK;
*portBPtr ^= LEDMASK << 1;
delay(500);
*portBPtr ^= LEDMASK << 1;
*portBPtr ^= LEDMASK << 2;
delay(500);
*portBPtr ^= LEDMASK << 2;
}
Apparently this is possible with only one delay function and following the requirements and constrains. I have been stuck on this for hours and cant figure it out.
I tried the following which also does not work because I'm unable to reset my counter back to zero
void loop() {
//use data register (portB) for controlling leds
*portBPtr ^= (LEDMASK << ledCount);
delay(500);
*portBPtr ^= (LEDMASK << ledCount);
ledCount++;
//cant figure out a way to reset ledCount back to 0 after 3 itterations
//running light only runs once so first led does not light after third led is lit
}
What am I missing?
Perhaps this:
*portBPtr |= (1 << ledCount);
delay(500);
*portBPtr &= ~(1 << ledCount);
ledCount = (9 >> (ledCount << 1)) & 3;
This also avoids ^ which is not in your list of allowed symbols.
The line ledCount = (9 >> (ledCount << 1)) & 3 is like a lookup table with each entry taking up two bits. It maps 0 to 1, 1 to 2, 2 to 0. The normal way to write this code is ledCount = (1 + ledCount) % 3;
This might also work, being a little simpler:
delay(500);
*portBPtr = (1 << ledCount);
ledCount = (9 >> (ledCount << 1)) & 3;
By using assignment rather than | or & or ^, all the other bits will be zero, presumably causing the non-current leds to turn off.

Why is not my SPI communication working? (Atmega644)

I'm building a drum machine, and I have stored a sample header file with a kick sound which takes values between 0 and 170. I want to send it over SPI to a 10-bit MCP4811 DAC which then outputs it to a 3.5mm audio jack.
I have my MISO,MOSI,SCK and RESET pins connected to my USB programmer as well as the DAC.
Here is a snippet of the audio file stored in "samples.h"
unsigned const char sample_1[2221] PROGMEM = {0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, ...}
unsigned int sample_len[1] = {2221}
So it is a sample of 2221 bits. This I want to send to the DAC using SPI with freq = 22 kHz.
I'm using a 16 MHz Crystal, so I set the fuses accordingly to use it.
I am using a Timer which overflows 22 kHz.
volatile unsigned int sample_count[1] = {0};
volatile unsigned int audio_out = 0;
volatile unsigned char spi_junk;
int main (void)
sei();
DDRB = 0b10110000; //Set MOSI, SCK and SS as output.
PORTB = (1 << PINB4) //active low on SS.
TIMSK1 = (1<<OCIE1A); //Enable interrupt
TCCR1B = (1<<WGM12) | (1<<CS11); // set CTC mode and divide clk by 8
OCR1A = 91; //16 MHz/(8*91) ~ 22068 Hz
//SPI Init
SPCR = (1<<SPE) | (1<<MSTR); //master, 8 MHz
SPSR = (1<<SPI2X);
ISR (TIMER1_COMPA_vect) {
audio_out = 0;
//If play_track == 1, then the sound should be played back.
if (play_track && sample_count[0] < sample_len[0]){
audio_out += (pgm_read_byte(&(sample_1[sample_count[0]++)));
// send audio_out to 10-bit DAC on SPI
PORTB &= ~(1<<PINB4); // B.4 (DAC /CS)
SPDR = (char) ((audio_out >> 6) & 0x000f); //byte 1 0 0 0 0 b9 b8 b7 b6
while (!(SPSR & (1<<SPIF)));
spi_junk = SPDR;
SPDR = (char) ((audio_out & 0x003f) << 2); //byte 2 b5 b4 b3 b2 b1 b0 0 0
while (!(SPSR & (1<<SPIF)));
spi_junk = SPDR;
PORTB |= (1<<PINB4);
}
My PIN set up is.
Atmega644 -> DAC
MOSI -> SDI
SCK -> SCK
SS -> /CS
On MCP4811
Vdd -> 5V
Vss -> GND
V_out -> Audio jack.
The rest of the pins on the MCP4811 are not connected to anything.
I have seen that the audio_out is working as expected, by displaying the audio_out value on a LCD screen. But nothing is being output to the DAC. Anyone see what could be wrong?
EDIT: Added the SPI init which I had missed to add.
Your line here
SPDR = (char) ((audio_out >> 6) & 0x000f); //byte 1 0 0 0 0 b9 b8 b7 b6
Will set ¬SHDN to 0 which will shutdown the DAC
0 = Shutdown the device. Analog output is not available. VOUT pin is connected to 500 kohm typical)
Set bit 12 to 1 instead
SPDR = (char) ((audio_out >> 6) & 0x0f)|0x10; //byte 1 0 0 0 1 b9 b8 b7 b6
from datasheet
1 = Active mode operation. VOUT is available.
There is no SPI initialization in your code.
add into main()
SPSR = (1 << SPI2X); // double speed (to get maximum of 8MHz output)
SPCR = (1 << SPE) | (1 << MSTR); // 1:1 prescaler, master mode, SPI mode 0, SPI enable
Also several remarks to your code:
use sei() only after all initializations are done, to avoid interrupts happened on uninitialized peripheral.
better first set PB4 high then setup it as output to avoid PB output low between two commands:
PORTB = (1 << PINB4) //active low on SS.
DDRB = 0b10110000; //Set MOSI, SCK and SS as output.

Arduino port relocation (PORTD to PORTB)

So I'm trying to connect the chip with an 8-bit data bus. All works good, but...
Arduino Uno uses D0 and D1 for serial TX/RX (USB connection with PC). When I try to open a serial connection (on serial.begin stage) ports D0 and D1 blocks and the chip stops working. This is a problem.
I think I should relocate pins D0 and D1 to another port. But the chip uses 8 pins (8bit chip). So I need to relocate the first pin (D0) and the second pin (D1) to port b. Maybe I can use B4 and B3?
But I don't know how to use PORTD[PD2, PD3, PD4, PD5, PD6, PD7] and PORTB[PB4, PB3] in one time like:
var portX = PORTD[PD2, PD3, PD4, PD5, PD6, PD7] + PORTB[PB4, PB3];
portX = data;
My code:
#include <avr/io.h> // For I/O and other AVR registers
#include <util/delay.h> // For timing
/* Pinmap (Arduino UNO compatible) */
#define YM_IC (5) // PC5 (= pin A5 for Arduino UNO)
#define YM_CS (4) // PC4 (= pin A4 for Arduino UNO)
#define YM_WR (3) // PC3 (= pin A3 for Arduino UNO)
#define YM_RD (2) // PC2 (= pin A2 for Arduino UNO)
#define YM_A0 (1) // PC1 (= pin A1 for Arduino UNO)
#define YM_A1 (0) // PC0 (= pin A0 for Arduino UNO)
#define YM_CTRL_DDR DDRC
#define YM_CTRL_PORT PORTC
#define YM_DATA_DDR DDRD
#define YD0 (0)(= pin D0 for Arduino UNO)
#define YD1 (1)(= pin D1 for Arduino UNO)
#define YD2 (2)(= pin D2 for Arduino UNO)
#define YD3 (3)(= pin D3 for Arduino UNO)
#define YD4 (4)(= pin D4 for Arduino UNO)
#define YD5 (5)(= pin D5 for Arduino UNO)
#define YD6 (6)(= pin D6 for Arduino UNO)
#define YD7 (7)(= pin D7 for Arduino UNO)
#define YM_DATA_PORT PORTD // Whole PORT D for data bus (= pins D0 to D7 for Arduino UNO)
#define YM_MCLOCK (1) // PB1 = OC1A (= pin D9 for Arduino UNO)
#define YM_MCLOCK_DDR DDRB
extern uint8_t chflash[6];
uint8_t dacflash;
#define NOTEONFLASH 4
#define VGMWAIT 15
static void write_ym(uint8_t data) {
YM_CTRL_PORT &= ~_BV(YM_CS); // CS LOW
YM_DATA_PORT = data;
_delay_us(1);
YM_CTRL_PORT &= ~_BV(YM_WR); // Write data
_delay_us(5);
YM_CTRL_PORT |= _BV(YM_WR);
_delay_us(5);
YM_CTRL_PORT |= _BV(YM_CS); // CS HIGH
}
/**
* Write data into a specific register of the YM2612
*
* #author Furrtek
* #param reg Destination register address
* #param data Data to write
*/
static void setreg(uint8_t reg, uint8_t data) {
YM_CTRL_PORT &= ~_BV(YM_A0); // A0 low (select register)
write_ym(reg);
YM_CTRL_PORT |= _BV(YM_A0); // A0 high (write register)
write_ym(data);
}
void call() {
setreg(0x28, 0xF0); // Key on
_delay_ms(1000);
setreg(0x28, 0x00); // Key off
}
uint8_t getfilebyte() {
return Serial.read();
}
int main() {
init();
//Serial.begin(9600);
/* Pins setup */
YM_CTRL_DDR |= _BV(YM_IC) | _BV(YM_CS) | _BV(YM_WR) | _BV(YM_RD) | _BV(YM_A0) | _BV(YM_A1);
YM_DATA_DDR |= _BV(YD0) | _BV(YD1) | _BV(YD2) | _BV(YD3) | _BV(YD4) | _BV(YD5) | _BV(YD6) | _BV(YD7);
YM_MCLOCK_DDR |= _BV(YM_MCLOCK);
YM_CTRL_PORT |= _BV(YM_IC) | _BV(YM_CS) | _BV(YM_WR) | _BV(YM_RD); /* IC, CS, WR and RD HIGH by default */
YM_CTRL_PORT &= ~(_BV(YM_A0) | _BV(YM_A1)); /* A0 and A1 LOW by default */
/* F_CPU / 2 clock generation */
TCCR1A = _BV(COM1A0); /* Toggle OCA1 on compare match */
TCCR1B = _BV(WGM12) | _BV(CS10); /* CTC mode with prescaler /1 */
TCCR1C = 0; /* Flag reset */
TCNT1 = 0; /* Counter reset */
OCR1A = 0; /* Divide base clock by two */
/* Reset YM2612 */
YM_CTRL_PORT &= ~_BV(YM_IC);
_delay_ms(10);
YM_CTRL_PORT |= _BV(YM_IC);
_delay_ms(10);
/* YM2612 Test code */
setreg(0x22, 0x00); // LFO off
setreg(0x27, 0x00); // Note off (channel 0)
setreg(0x28, 0x01); // Note off (channel 1)
setreg(0x28, 0x02); // Note off (channel 2)
setreg(0x28, 0x04); // Note off (channel 3)
setreg(0x28, 0x05); // Note off (channel 4)
setreg(0x28, 0x06); // Note off (channel 5)
setreg(0x2B, 0x00); // DAC off
setreg(0x30, 0x71); //
setreg(0x34, 0x0D); //
setreg(0x38, 0x33); //
setreg(0x3C, 0x01); // DT1/MUL
setreg(0x40, 0x23); //
setreg(0x44, 0x2D); //
setreg(0x48, 0x26); //
setreg(0x4C, 0x00); // Total level
setreg(0x50, 0x5F); //
setreg(0x54, 0x99); //
setreg(0x58, 0x5F); //
setreg(0x5C, 0x94); // RS/AR
setreg(0x60, 0x05); //
setreg(0x64, 0x05); //
setreg(0x68, 0x05); //
setreg(0x6C, 0x07); // AM/D1R
setreg(0x70, 0x02); //
setreg(0x74, 0x02); //
setreg(0x78, 0x02); //
setreg(0x7C, 0x02); // D2R
setreg(0x80, 0x11); //
setreg(0x84, 0x11); //
setreg(0x88, 0x11); //
setreg(0x8C, 0xA6); // D1L/RR
setreg(0x90, 0x00); //
setreg(0x94, 0x00); //
setreg(0x98, 0x00); //
setreg(0x9C, 0x00); // Proprietary
setreg(0xB0, 0x32); // Feedback/algorithm
setreg(0xB4, 0xC0); // Both speakers on
setreg(0x28, 0x00); // Key off
setreg(0xA4, 0x22); //
setreg(0xA0, 0x69); // Set frequency
call();
while(1000) {
if (Serial.available() > 0) {
_delay_ms(1000);
call();
_delay_ms(1000);
char var = getfilebyte();
Serial.print("Yeah: ");
Serial.println(var);
} else {
Serial.print("Nooo: ");
Serial.println(Serial.available());
call();
}
}
while(1);
}
You need bitmath to put them together. Bitshift and bitwise or and bitwiase and. Look those up in the reference. What you want will look something like:
byte portX = (PORTD & 0xFC) | ((PORTB >> 3) & 0x03);
What you actually end up with might be slightly different though since I am making assumptions about what order you want the bits in. The first part masks off the high 6 bits of PORTD, the second part shift the two bits you want out of PORTB into the lowest two bits and masks them off. The | in the middle puts the two together.
A seceond answer because I think you're actually going the other way now that I re-read your question. I think you want to output to those pins. You'll need bitmath to do that as well.
Something like:
PORTB |= (data & 0x03) << 3;
PORTD |= data & 0xFC;
"There is no way to write to two different registers in the same instruction, so if you need all eight bits to change exactly at the same time you're out of luck." Answer from electronics. Maybe i can test my luck in multiple serial begin and end..

how to make a 3D mask

Currently I meet one technique issue, which makes me want to improve the previous implementation, the situation is:
I have 5 GPIO pins, I need use these pins as the hardware identifier, for example:
pin1: LOW
pin2: LOW
pin3: LOW
pin4: LOW
pin5: LOW
this means one of my HW variants, so we can have many combinations. In previous design, the developer use if-else to implement this, just like:
if(PIN1 == LOW && ... && ......&& PIN5 ==LOW)
{
HWID = variant1;
}
else if( ... )
{
}
...
else
{
}
but I think this is not good because it will have more than 200 variants, and the code will become to long, and I want changed it to a mask. The idea is I treat this five pins as a five bits register, and because I can predict which variant I need to assign according to GPIOs status(this already defined by hardware team, they provide a variant list, with all these GPIO pins configuration), therefore, the code may look like this:
enum{
variant 0x0 //GPIO config 1
...
variant 0xF3 //GPIO config 243
}
then I can first read these five GPIO pins status, and compare to some mask to see if they are equal or not.
Question
However, for GPIO, it has three status, namely: LOW, HIGH, OPEN. If there is any good calculation method to have a 3-D mask?
You have 5 pins of 3 states each. You can approach representing this in a few ways.
First, imagine using this sort of framework:
#define LOW (0)
#define HIGH (1)
#define OPEN (2)
uint16_t config = PIN_CONFIG(pin1, pin2, pin3, pin4, pin5);
if(config == PIN_CONFIG(LOW, HIGH, OPEN, LOW, LOW))
{
// do something
}
switch(config) {
case PIN_CONFIG(LOW, HIGH, OPEN, LOW, HIGH):
// do something;
break;
}
uint16_t config_max = PIN_CONFIG(OPEN, OPEN, OPEN, OPEN, OPEN);
uint32_t hardware_ids[config_max + 1] = {0};
// init your hardware ids
hardware_ids[PIN_CONFIG(LOW, HIGH, HIGH, LOW, LOW)] = 0xF315;
hardware_ids[PIN_CONFIG(LOW, LOW, HIGH, LOW, LOW)] = 0xF225;
// look up a HWID
uint32_t hwid = hardware_ids[config];
This code is just the sort of stuff you'd like to do with pin configurations. The only bit left to implement is PIN_CONFIG
Approach 1
The first approach is to keep using it as a bitfield, but instead of 1 bit per pin you use 2 bits to represent each pin state. I think this is the cleanest, even though you're "wasting" half a bit for each pin.
#define PIN_CLAMP(x) ((x) & 0x03)
#define PIN_CONFIG(p1, p2, p3, p4, p5) \\
(PIN_CLAMP(p1) & \\
(PIN_CLAMP(p2) << 2) & \\
(PIN_CLAMP(p3) << 4) & \\
(PIN_CLAMP(p4) << 6) & \\
(PIN_CLAMP(p5) << 8))
This is kind of nice because it leaves room for a "Don't care" or "Invalid" value if you are going to do searches later.
Approach 2
Alternatively, you can use arithmetic to do it, making sure you use the minimum amount of bits necessary. That is, ~1.5 bits to encode 3 values. As expected, this goes from 0 up to 242 for a total of 3^5=243 states.
Without knowing anything else about your situation I believe this is the smallest complete encoding of your pin states.
(Practically, you have to use 8 bits to encode 243 values, so it's higher 1.5 bits per pin)
#define PIN_CLAMP(x) ((x) % 3) /* note this should really assert */
#define PIN_CONFIG(p1, p2, p3, p4, p5) \\
(PIN_CLAMP(p1) & \\
(PIN_CLAMP(p2) * 3) & \\
(PIN_CLAMP(p3) * 9) & \\
(PIN_CLAMP(p4) * 27) & \\
(PIN_CLAMP(p5) * 81))
Approach 1.1
If you don't like preprocessor stuff, you could use functions a bit like this:
enum PinLevel (low = 0, high, open);
void set_pin(uint32_t * config, uint8_t pin_number, enum PinLevel value) {
int shift = pin_number * 2; // 2 bits
int mask = 0x03 << shift; // 2 bits set to on, moved to the right spot
*config &= ~pinmask;
*config |= (((int)value) << shift) & pinmask;
}
enum PinLevel get_pin(uint32_t config, uint8_t pin_number) {
int shift = pin_number * 2; // 2 bits
return (enum PinLevel)((config >> shift) & 0x03);
}
This follows the first (2 bit per value) approach.
Approach 1.2
YET ANOTHER WAY using C's cool bitfield syntax:
struct pins {
uint16_t pin1 : 2;
uint16_t pin2 : 2;
uint16_t pin3 : 2;
uint16_t pin4 : 2;
uint16_t pin5 : 2;
};
typedef union pinconfig_ {
struct pins pins;
uint16_t value;
} pinconfig;
pinconfig input;
input.value = 0; // don't forget to init the members unless static
input.pins.pin1 = HIGH;
input.pins.pin2 = LOW;
printf("%d", input.value);
input.value = 0x0003;
printd("%d", input.pins.pin1);
The union lets you view the bitfield as a number and vice versa.
(note: all code completely untested)
This is my suggestion to solve the problem
#include<stdio.h>
#define LOW 0
#define HIGH 1
#define OPEN 2
#define MAXGPIO 5
int main()
{
int gpio[MAXGPIO] = { LOW, LOW, OPEN, HIGH, OPEN };
int mask = 0;
for (int i = 0; i < MAXGPIO; i++)
mask = mask << 2 | gpio[i];
printf("Masked: %d\n", mask);
printf("Unmasked:\n");
for (int i = 0; i < MAXGPIO; i++)
printf("GPIO %d = %d\n", i + 1, (mask >> (2*(MAXGPIO-1-i))) & 0x03);
return 0;
}
A little explanation about the code.
Masking
I am using 2 bits to save each GPIO value. The combinations are:
00: LOW
01: HIGH
02: OPEN
03 is Invalid
I am iterating the array gpio (where I have the acquired values) and creating a mask in the mask variable shifting left 2 bits and applying an or operation.
Unmasking
To get the initial values I am just making the opposite operation shifting right 2 bits multiplied by the amount of GPIO - 1 and masking with 0x03
I am applying a mask with 0x03 because those are the bit I am interested.
This is the result of the program
$ cc -Wall test.c -o test;./test
Masked: 38
Unmasked:
GPIO 1 = 0
GPIO 2 = 0
GPIO 3 = 2
GPIO 4 = 1
GPIO 5 = 2
Hope this helps

understanding MSB LSB

I am working on converting a program that runs on a specific micro-controller and adapt it to run on the raspberry pi. I have successfully been able to pull values from the sensor I have been working with but now I have run into a problem and I think it is caused by a few lines of code I am having trouble understanding. I have read up on what they are but am still scratching my head. The code below I believe is supposed to modify the number that gets stored in the X,Y,Z variables however I don't think this is occurring in my current program. Also I had to change byte to an INT to get the program to compile with out errors. This is the unmodified code from the original code I have converted. Can someone tell me if this is even modifying the number in anyway?
void getGyroValues () {
byte MSB, LSB;
MSB = readI2C(0x29);
LSB = readI2C(0x28);
x = ((MSB << 8) | LSB);
MSB = readI2C(0x2B);
LSB = readI2C(0x2A);
y = ((MSB << 8) | LSB);
MSB = readI2C(0x2D);
LSB = readI2C(0x2C);
z = ((MSB << 8) | LSB);
}
Here is the original readI2C function:
int readI2C (byte regAddr) {
Wire.beginTransmission(Addr);
Wire.write(regAddr); // Register address to read
Wire.endTransmission(); // Terminate request
Wire.requestFrom(Addr, 1); // Read a byte
while(!Wire.available()) { }; // Wait for receipt
return(Wire.read()); // Get result
}
I2C is a 2-wire protocol used to talk to low-speed peripherals.
Your sensor should be connected over the I2C bus to your CPU. And you're reading 3 values from the sensor - x, y and z. The values for these are accessible from the sensor as 6 x 8-bit registers.
x - Addresses 0x28, 0x29
y - Addresses 0x2A, 0x2B
z - Addresses 0x2C, 0x2D
ReadI2C() as the function name implies, reads a byte of data from a given address from your sensor and returns the data being read. The code in ReadI2C() is dependent on how your device's I2C controller is setup.
A byte is 8-bits of data. The MSB (Most-Significant-Byte) and LSB(Least-Significant-Byte) denote 8-bits each read over I2C.
It looks like you're interested in a 16-bit data (for x, y and z). To construct the 16-bit data from the 2 pieces of 8-bit data, you shift the MSB by 8-bits to the left and then perform a logical-OR operation with the LSB.
For example:
Let us assume: MSB = 0x45 LSB = 0x89
MSB << 8 = 0x4500
(MSB << 8) | LSB = 0x4589
Look at my comments inline as well:
void getGyroValues () {
byte MSB, LSB;
MSB = readI2C(0x29);
LSB = readI2C(0x28);
// Shift the value in MSB left by 8 bits and OR with the 8-bits of LSB
// And store this result in x
x = ((MSB << 8) | LSB);
MSB = readI2C(0x2B);
LSB = readI2C(0x2A);
// Do the same as above, but store the value in y
y = ((MSB << 8) | LSB);
MSB = readI2C(0x2D);
LSB = readI2C(0x2C);
// Do the same as above, but store the value in z
z = ((MSB << 8) | LSB);
}

Resources