Prima cosa da sapere prima di iniziare è che le periferiche nei processori arm sono memory mapped quindi per accedere alle periferiche (come le digitali) basta leggere ad un indirizzo prefissato in memoria. Per conoscere l'indirizzo di memoria corretto basta leggere il datasheet del processore.
Leggendo i datasheet del processore in questione si può notare che le periferiche di I/O sono suddivise in 8 porte differenti da 8 bit ciascuno (chiamate con le lettere dalla A alla H). Alcune uscite sono assegnate a piu' device tramite multiplexer, e quindi configurabili scrivendo in un'apposita area di memoria di 32 bit chiamata device_config. Inoltre per ogni digitale si deve settare un'altra area di memoria che indica se quella digitale sara' usata come input o come output.
Avevo letto in giro gente che usava push e pop scritte in assembler per scrivere e leggere in memoria, e io non capivo il perché, quindi ho provato a fare di testa mia e ad usare i puntatori, e tutto funziona a meraviglia ed il codice risulta IMHO molto piu' leggibile:
// Dichiaro i puntatori che dovranno puntare all'indirizzo di memoria // in cui sono mappate le digitali static volatile uint8_t* port[8], *portd[8]; // Apro /dev/mem int devmem = open("/dev/mem", O_RDWR|O_SYNC); assert(devmem != -1); // Prendo la pagina di memoria relativa alle digitali data_page = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE, MAP_SHARED, devmem, DATA_PAGE); assert (&data_page != MAP_FAILED); // Prendo la pagina di memoria relativa alle configurazioni syscon_page = (unsigned long)mmap(0, getpagesize(), PROT_READ|PROT_WRITE , MAP_SHARED, devmem, SYSCON_PAGE); assert(&syscon_page != MAP_FAILED); // Prendo l'area di memoria relativa ai device config uint32_t* deviceCfg = (uint32_t*) (syscon_page + DEVICECFG); // Abilito la scrittura in memoria. uint32_t* syscon_unlock = (uint32_t*) (syscon_page + SYSCON_UNLOCK); *syscon_unlock = UNLOCK_VAL; //Associo ai puntatori l'area di memoria relativa alle porte digitali port[0] = (uint8_t*) (data_page + PADR); //[...] port[7] = (uint8_t*) (data_page + PHDR); //Associo ai puntatori l'area di memoria relativa alla direzione delle porte digitali portd[0] = (uint8_t*) (data_page + PADDR); //[...] portd[7] = (uint8_t*) (data_page + PHDDR);
E adesso non serve altro che leggere e scrivere nelle aree di memoria:
inline void set_bit(bool src, uint8_t *dst, uint8_t bit) { int value = 1 << bit; if(src) *dst |= value; else *dst &= ~value; } inline void get_bit(uint8_t src, bool *dst, uint8_t bit) { int value = 1 << bit; *dst = (src & value) >> bit; } int set_port_bit (uint8_t nport, bool value, uint8_t pos) { if(nport < 8 && pos < 8) { set_bit(value, port[nport], pos); return 0; } return -1; } int get_port_bit (uint8_t nport, bool* value, uint8_t pos) { if(nport < 8 && pos < 8) { get_bit(*port[nport], value, pos); return 0; } return -1; } int set_port_dir_bit (uint8_t nport, bool value, uint8_t pos) { if(nport < 8 && pos < 8) { set_bit(value, portd[nport], pos); return 0; } return -1; }
PS: Se vi servono maggiori informazioni basta chiedere vi email: me (at) mancausoft (dot) org