Problema bitshift uint32_t

Buongiorno a tutti.
Sto lavorando ad un pezzo di codice che mi dovrebbe permettere di valorizzare una variabile di tipo uint32_t (a mo di registro PINx)con lo stato di alcuni ingressi di un micro.
Alcuni di questi ingressi sono fisicamente sulle porte del micro, altri risiedono in un port expander i2c .

Ho deciso di andare ad assemblare il valore di quel registro effettuando operazioni di 'bitshift' e 'and' per formarne il valore :

uint8_t  port_a;
uint8_t  port_b;
uint8_t internal_inputs =((PINB & 0x06)>>1) + (((PIND & 0xf0)>>4)<< 2) + (((PINC & 0x0c)>>2)<< 6);
_mux.GetMuxStatus(&port_a,&port_b);
Serial.println(port_a);
Serial.println(port_b);
Serial.println(internal_inputs);
uint32_t current_bus = (port_a + (port_b <<8) + (internal_inputs << 12)& 0xfffff;
Serial.println( current_bus);

Con tutti i pin degli ingressi alti (pull'up esterni) ottengo correttamente i valori di port_a,port_b e internal_inputs.
Quando vado a variare lo stato di un ingresso di port_a o port_b , varia il suo valore relativo(port_a o port_b ovviamente) e varia il valore anche di current_bus.
Se invece vado a variare lo stato di un pin collegato direttamente al micro, varia correttamente internal_inputs , ma sorpresa delle sorprese current_bus non cambia.
E' un limite di architettura del micro o mi è sfuggito qualcosa?(l'operazione di shift su internal_inputs penso coinvolga 3 byte)

Grazie in anticipo.

Dato che le variabili associate ai port sono unsigned char devi usare il casting per promuoverle come unsigned int, o long, prima di effettuare lo shift altrimenti il loro valore diventa 0 perché i bit shiftati vanno persi.

uint32_t current_bus = (port_a +  ((unsigned int) port_b <<8) + (unsigned int)((unsigned long) internal_inputs << 12)& 0xfffff;

Ok, grazie, lo provo subito.
Supponevo che mi ero perso qualche pezzo.

il punto che

uint8_t internal_inputs =((PINB & 0x06)>>1) + (((PIND & 0xf0)>>4)<< 2) + (((PINC & 0x0c)>>2)<< 6);

non crei problemi è dato dal fatto che nonostante lo shift di bit il valore non sfora mai il byte di lunghezza?

niki77:
non crei problemi è dato dal fatto che nonostante lo shift di bit il valore non sfora mai il byte di lunghezza?

Il problema lo hai solo se shifti verso sinistra perché presuppone un aumento del valore numerico, a meno che non lo fai apposta per perdere bit o traslare un indirizzo come nel caso della I2C, mentre se shifti verso destra il valore numerico diminuisce e non serve il casting.

Ho utilizzato quella tecnica per mascherare e portare in posizione dei bit di alcuni registri in maniera tale da avere omogeneamente una variabile contenente i valori ordinati come fanno comodo a me, penso e spero sia codice efficiente , del resto dovrebbero essere tutte istruzioni native.

Ho capito perfettamente quindi se faccio:

byte a = 0xff;

a= (a <<4) >>4 ; mi perdo i 4 bit più significativi perchè prima li sposta verso sx di 4 posizioni , quindi fuori dalla portata del tipo byte, poi li riporta a dx ma a quel punto se li è già persi pertanto non li può recuperare.

se invece di byte utilizzo uint16_t, questo non accade.

Ho erroneamente pensato (e poi pensandoci bene non so neanche su che basi) che l'importante fosse che il valore dell'operazione finita stesse nei limiti del tipo di dato utilizzato.

Grazie Astro, contributo prezioso come sempre.

E se usassi una union?

union UnionOfPins {
 port_a:8;
 port_b:8;
 internal_inputs:16;
 uint32_t myPort;
} myUnionOfPins;
 myUnionOfPins.myPort = 0xff;

Se usassi una union come quella che hai scritto tu penso che dovrei portarmi dietro 8 byte invece di 4.

Potrei aver detto una castroneria, non uso molto le union.
Comunque non hai notato bene come ho assemblato i valori, in realtà io vado a formare una variabile a 20bit utili, di cui ogni bit corrisponde esattamente al suo numero di ingresso.
Con la union che proponi, myPort conterrebbe comunque 4 bit inutili di port_b.

Poi non trovo corretto questo :

myUnionOfPins.myPort = 0xff;

Eventualmente dovrebbe essere 0xfffff (20 bit)