Arduino UNO R4 - manipolazione diretta dei pin di I/O

Non riguarda propriamente gli I/O però, dato che parliamo comunque di ingressi dei RA4M1, continuo a dare indicazioni in questa discussione ...

... questa volta vediamo un "problemino" relativo gli ingressi analogici della nuova MCU. Chi ha lavorato con Arduino classico (AVR) ricorderà che quando si connette un qualche cosa agli ingressi analogici, l'impedenza in ingresso, per avere misure affidabili e nei giusti tempi di carica del S/H, deve essere attorno ai 50 KΩ, difatti , il datasheet del ATmega328P riporta:

... con il Renesas Ra4M1 la cosa si ... complica ... infatti l'impedenza in ingresso deve essere circa ... un fattore dieci più bassa ! Riporto dal datasheet...

... tenetene conto quando progettate applicazioni con segnali analogici. :roll_eyes:

Guglielmo

Continuo invece la descrizione dell'accesso diretto hai pin di I/O ... questa volta, invece che utilizzare la varie #define che ho preparato, usando i nomi dei registri che il compilatore è in grado di interpretare ...

... anticipo subito che, pur ottenendo alla fine lo stesso risultato, secondo me, usare le #define che ho preparato, rende tutto più semplice e chiaro, comunque, volendo andare a leggere e scrivere direttamente su un pin si può usare la sintassi:

R_PFS->PORT[port_number].PIN[pin_number].PmnPFS

Esempio, per mettere alto il pin D13 di Arduino UNO R4 MINIMA, sullo schema dobbiamo verificare a che PORT e PIN corrisponde e vediamo che è su PORT 1, PIN 11 e poi possiamo scrivere:

R_PFS->PORT[1].PIN[11].PmnPFS = 0x05;

... vedete voi se è più semplice o più complicato da scrivere :wink:

Guglielmo

1 Like

Continuando lo studio del datasheet e degli altri documenti ho approfondito l'uso dei nomi dei registri che riconosce il compilatore ...

... prima di tutto occorre tenere conto del fatto che il sistema include automaticamente il file relativo alla "descrizione" della MCU, che, per i nostri RA4M1 è il seguente: R7FA4M1AB.zip (121.0 KB) ... è un file di quasi 18'000 righe che contiene tutte le definizioni dei registri della MCU con varie "union" che permettono di verderli in vario modo. Essi difatti possono essere vsiti o complessivamente nel loro insieme o, con un nome diverso, con l'accesso diretto, per nome, ai vari bit .

Prendiamo ad esempio il registro "VBATT Control Register 2" (Rif. Renesas RA4M1 User's Manual cap. 11.2.2), nel documento esso è identificato come:

SYSTEM.VBTCR2 4001 E4B0h

... ovvero, è un registro di sistema, si chiama VBTCR2 e si trova all'indirizzo di memoria 0x4001E4B0

Ora, tale registro, può essere visto nella sua interezza come:

R_SYSTEM->VBTCR2

... e, con tale nome, può essere letto o scritto (è protetto in scrittura, se occorre bisogna abilitarlo in scrittura) nella sua interezza (uint8_t), ma ... esso può essere anche visto con i nomi dei bit che lo compongono (così come descritti nella documentazione) usando la nomenclatura:

R_SYSTEM->VBTCR2_b.nomeDelBit

... ovvero, ad esempio, per mettere a 0 il bit che si chiama VBTLVDEN si può scrivere:

R_SYSTEM->VBTCR2_b.VBTLVDEN = 0;

... che è abbastanza comodo e può evitare errori :wink:

Questo vale per tutti i vari registri che si trovano nella documentazione che ... va studiata con attenzione per ogni minima cosa si voglia fare; ci difatti sono registri che sono protetti in scrittura per cui prima di poterli scrivere occorre abilitarne la suddetta scrittura, altri che richiedono una precisa sequenza (prima l'impostazione di alcuni bit di un registro e successivamente l'altra di un altro registro) altri ancora che, per la loro modifica, richiedono di attendere sino a quando un bit in un altro registro, non assuma un certo valore ... :roll_eyes:

Insomma ... è una MCU abbastanza complessa e occorre veramente studiare bene la documentazione prima di lavorarci a livello "Bare Metal" :wink:

Guglielmo

1 Like

Come forse averete visto, la UNO R4 WiFi è dotata di un pin per il collegamento di una batteria di backup (che permette di mantenere in funzione l'RTC e i 512 bytes di memoria RAM associata ed ad uso dell'utente) ...

... purtroppo, chi ha provato, avrà visto che in realtà, pur avendo la batteria collegata, al distacco/riattacco dell'alimentazione, il RTC perde la programmazione ed idem per la battery-backuped RAM :confused:

Questo avviene perché, di base, la batteria di backup è disabilitata e quindi, occorre abilitarla.

Per fare questo, basta inserire questa piccola funzione nel vostro .ino:

void vbatt_setup ( void ) {
  R_BSP_RegisterProtectDisable(BSP_REG_PROTECT_OM_LPC_BATT);
  if ( 1 != R_SYSTEM->VBTSR_b.VBTRVLD ) R_BSP_SoftwareDelay(10, BSP_DELAY_UNITS_MILLISECONDS);
  R_SYSTEM->VBTCR2_b.VBTLVDEN = 0;
  R_SYSTEM->VBTCR1_b.BPWSWSTP = 0;
  R_BSP_RegisterProtectEnable(BSP_REG_PROTECT_OM_LPC_BATT);
}

... ed, all'inizio del setup(), richiamarla:

void setup ( ) {
   vbatt_setup ( );
   ...
   ...
}

... e la batteria di backup farà il suo lavoro :grin:

Spero che la cosa vi sia utile, buon divertimento !

Guglielmo

3 Likes

Ancora un piccolo esempio di utilizzo diretto dei registi e delle funzioni messe a disposizione del Renesas FSP (Flexible Software Package) ... Blink senza utilizzo del framework Arduino ... :grin:

# if !defined ( ARDUINO_UNOR4_WIFI ) && !defined ( ARDUINO_UNOR4_MINIMA )
#error "This code is only for Arduino UNO R4"
#endif

#define  R4_LED  R_PFS->PORT[1].PIN[11].PmnPFS

#define IO_OUT_LOW  0x04
#define IO_OUT_HIGH 0x05

void setup() {
  R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS);

}

void loop() {
  static bool ledStatus = false;
  //
  if ( ledStatus ) {
    R4_LED = IO_OUT_LOW;
  } else {
    R4_LED = IO_OUT_HIGH;
  }
  ledStatus = !ledStatus;
  R_BSP_SoftwareDelay(500, BSP_DELAY_UNITS_MILLISECONDS);
}

Guglielmo

1 Like

Una segnalazione ...
... un utente del forum internazionale ha segnalato un errore che si trova in un file del "core" di Arduino UNO R4.

il file in oggetto è: packages/arduino/hardware/renesas_uno/1.2.0/cores/arduino/digital.cpp

... le righe che contengono l'errore sono le seguenti:

case OUTPUT_OPENDRAIN:
			R_IOPORT_PinCfg(NULL, g_pin_cfg[pin].pin, IOPORT_CFG_PORT_DIRECTION_OUTPUT | IOPORT_CFG_PMOS_ENABLE);

... che abilitano il pin come "PMOS open-drain" e che devono diventare invece:

case OUTPUT_OPENDRAIN:
			R_IOPORT_PinCfg(NULL, g_pin_cfg[pin].pin, IOPORT_CFG_PORT_DIRECTION_OUTPUT | IOPORT_CFG_NMOS_ENABLE);

... affinché con la pinMode (pin, OUTPUT_OPENDRAIN); il pin venga correttamente configurato come "NMOS open-drain".

Guglielmo

Ho fatto una piccola tabella riepilogativa con i pin del RA4M1 e l'equivalente su R4 MINIMA ed R4 WiFi ... i pin marcati in giallo (nella colonna Port) sono quelli che il RA4M1 ha ma che non sono accessibili sugli Arduino R4, quelli marcati in verde sono invece i pin con capacità PWM (dalla documentazione ufficiale Arduino) ... spero possa essere utile.

RA4M1_64 pins.pdf (37.4 KB)

Naturalmente, se trovate degli errori o delle inesattezze, segnalatemelo e provvedo a correggere :slight_smile:

Ah, i port marcati N.A. sono port che sulla versione a 64 pin NON esistono, ma esistono nella versione a 100 pin del chip :wink:

Guglielmo

1 Like

Un chiarimento relativamente a come viene caricato il codice negli Arduino UNO R4 ...

... contrariamente agli altri Arduino classici, in cui occorreva selezionare esattamente la porta seriale con cui si caricava il programma nella flash della MCU, con Arduino UNO R4 la cosa NON è più vera dato che il caricamento del codice in flash NON avviene tramite la seriale, ma via porta USB utilizzando la modalità DFU (Device Firmware Upgrade) che è una cosa implementata a livello USB le cui specifiche sono descritte QUI.

L'utility per il caricamento del codice tramite DFU è la dfu-util (Device Firmware Upgrade Utilities) che è spiegata QUI.

Guglielmo

Non riguarda specificatamente i pin di I/O, ma, senza voler fare pubblicità, segnalo che il 28 e 29 Ottobre 2024, "Futura Academy" organizza l'unico corso (in questo caso un webinar) in lingua Italiana relativo all'uso di FreeRTOS™ e basato su Arduino UNO R4 WiFi. :slight_smile:

Per tutti i dettagli potete leggere QUI ...
... aggiungo che, per gli utenti del forum Arduino di lingua Italiana, sono riuscito ad avere un codice sconto (che si somma alla promozione prenota prima), che da diritto ad un 10%; al momento della prenotazione del corso occorre indicare il codice promozionale:

7Q6WD4RC

Spero possa interessare ...

Guglielmo

Grazie

Mi è capitato di leggere QUESTA discussione nella sezione di lingua Inglese e ... incuriosito ho voluto fare un paio di prove anche io evitando però di utilizzare, per il I/O, il lento framework Arduino, ma scrivendo direttamente sulle porte.

Questo il codice che ho utilizzato (così potete provare anche voi):

/*
 * for test we use pin 7, PD7 on UNO, P107 on UNO R4 MINIMA
 */
 
void setup() {

#if defined (ARDUINO_AVR_UNO)
#pragma message "Compiling for Arduino UNO R3"

  DDRD |=   (1<<7); // set PD7 as OUTPUT
loop_uno:
  PORTD |=  (1<<7); // set PD7 HIGH
  PORTD &= ~(1<<7); // set PD7 LOW
  goto loop_uno;

#elif defined (ARDUINO_UNOR4_MINIMA)
#pragma message "Compiling for Arduino UNO R4"

#define PORTBASE 0x40040000
#define PFS_P107PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 7 * 4)))
#define IO_D7  *PFS_P107PFS_BY
#define IO_OUT_HIGH  0x05
#define IO_OUT_LOW   0x04

loop_uno_r4:
  IO_D7 = IO_OUT_HIGH;
  IO_D7 = IO_OUT_LOW;
  goto loop_uno_r4;

#else
#error "Unsupported board"
#endif

}

void loop() {

}

... non solo, per evitare possibili ritardi introdotti da classici "cicli" ho utilizzato l'abominevole ( :joy:) "goto" per saltare nel loop in cui metto HIGH e LOW un pin di I/O.

Da un lato abbiamo una MCU a 8 bit (ATmega328P), che esegue la maggior parte delle operazioni assembler in UN singolo ciclo macchina e che ha un clock a 16 MHz, dall'altra abbiamo una MCU ARM a 32 bit (R7FA4M1AB3CFM), molto più complessa, con un clock a 48 MHz.

Il risultato si allontana parecchio da quanto riportato nella discussione iniziale di cui ho messo il link ed è abbastanza vicino a quanto mi aspettavo ...

... la frequenza dell'onda quadra in uscita sul pin 7 di Arduino UNO R3 arriva a poco più di 2.6 MHz, mentre quella in uscita sul pin 7 di Arduino UNO R4 arriva a poco meno di 6MHz.

Il rapporto tra il clock della R4 ed R3 è 3, quello della frequenza in uscita è circa 2.25 penserei leggermente penalizzato dal fatto che la MCU lavora a 32 bit e quindi, probabilmente, gli indirizzamenti impiegano qualche ciclo macchina di più.

Nel post successivo riporto sia il confronto del codice assembler generato che le immagini sull'oscilloscopio dei due segnali.

Guglielmo

Questo l'estratto dal disassemblato del .elf generato dai due diversi compilatori:

ATmega328P:

#if defined (ARDUINO_AVR_UNO)
#pragma message "Compiling for Arduino UNO R3"

DDRD |= (1<<7); // set PD7 as OUTPUT
96e: 57 9a sbi 0x0a, 7 ; 10
loop_uno:
PORTD |= (1<<7); // set PD7 HIGH
970: 5f 9a sbi 0x0b, 7 ; 11
PORTD &= ~(1<<7); // set PD7 LOW
972: 5f 98 cbi 0x0b, 7 ; 11
974: fd cf rjmp .-6 ; 0x970 <__stack+0x71>

RA4M1:

00004100 :
#define IO_D7 *PFS_P107PFS_BY
#define IO_OUT_HIGH 0x05
#define IO_OUT_LOW 0x04

loop_uno_r4:
IO_D7 = IO_OUT_HIGH;
4100: 4b03 ldr r3, [pc, #12] ; (4110 <setup+0x10>)
4102: f04f 0105 mov.w r1, #5
IO_D7 = IO_OUT_LOW;
4106: 2204 movs r2, #4
IO_D7 = IO_OUT_HIGH;
4108: 7019 strb r1, [r3, #0]
IO_D7 = IO_OUT_LOW;
410a: 701a strb r2, [r3, #0]
410c: e7fc b.n 4108 <setup+0x8>
410e: bf00 nop
4110: 4004085f .word 0x4004085f

Questa l'uscita dalla UNO R3:

... e questa in uscita dalla UNO R4:

Guglielmo

P.S.: Mi scuso per la scarsa qualità delle immagine, ma son o foto fatte allo schermo del oscilloscopio con il telefonino :grin:

Hai usato la sonda x1?... La banda massima dichiarata delle sonda x1/x10 si riferisce alla posizione x10.

Comunque, su Arduino Uno R3 puoi invertire lo stato scrivendo 1 su PIND! Che frequenza ottieni?

loop_uno:
  PIND |=  (1<<7); // Inverte lo stato di PD7.
  goto loop_uno;

Forse così è più veloce?

loop_uno:
  PIND = 0xF0; // Inverte lo stato di PD7.
  goto loop_uno;

Si (e probabilmente neanche ben regolata come capacità) ... ma non mi interessava la "forma d'onda", ma il periodo :wink:

Lo so bene :smiling_imp:, ma ... equivalente funzione NON esiste sul RA4M1 e, volendo fare un test il più possibile comparatorio, ho evitato di usare cose che su una MCU esistono e sull'altra no ... NON mi interessava la velocità massima, ma il confronto a parità di operazioni!

Guglielmo

Sarebbe anche interessante vedere le differenze di velocità USANDO il framework, per capire quanto più lento è rispetto alle porte dirette, ma ANCHE per vedere se il "rapporto" di maggiore velocità della R4 rimane uguale con il framework in mezzo ai maroni. :grin:

... modifico il programma e faccio qualche altra prova :grin:

Guglielmo

1 Like

Questo è il codice modificato così potete provarlo:

/*
 * for the test we use pin 7, PD7 on UNO, P107 on UNO R4
 */

#define USE_FRAMEWORK 1  // comment this row to use direct I/O
 
void setup() {
	
#if defined (USE_FRAMEWORK)
#pragma message "Compiling using Arduino Framework"
	
  pinMode (7, OUTPUT);
loop_frw:
  digitalWrite (7, HIGH);
  digitalWrite (7, LOW);
  goto loop_frw;

#else

#if defined (ARDUINO_AVR_UNO)
#pragma message "Compiling for Arduino UNO R3"

  DDRD |=   (1<<7); // set PD7 as OUTPUT
loop_uno:
  PORTD |=  (1<<7); // set PD7 HIGH
  PORTD &= ~(1<<7); // set PD7 LOW
  goto loop_uno;

#elif defined (ARDUINO_UNOR4_MINIMA)
#pragma message "Compiling for Arduino UNO R4"

#define PORTBASE 0x40040000
#define PFS_P107PFS_BY ((volatile unsigned char  *)(PORTBASE + 0x0843 + ( 7 * 4)))
#define IO_D7  *PFS_P107PFS_BY
#define IO_OUT_HIGH  0x05
#define IO_OUT_LOW   0x04

loop_uno_r4:
  IO_D7 = IO_OUT_HIGH;
  IO_D7 = IO_OUT_LOW;
  goto loop_uno_r4;

#else
#error "Unsupported board"
#endif
  
#endif  // USE_FRAMEWORK

}

void loop() {

}

... non ricordavo che il framework Arduino fosse così penalizzante ... i risultati sono veramente terribili (se paragonati con il I/O diretto) ...

... la frequenza dell'onda quadra in uscita sul pin 7 di Arduino UNO R3 arriva a poco più di 150 KHz :sob:, mentre quella in uscita sul pin 7 di Arduino UNO R4 arriva a poco più di 724 KHz :cry: con un rapporto di circa 4.8, da cui ... almeno nella parte I/O il framework Arduino per R4 sembra dare risultati migliori di quello per R3 :roll_eyes:

Nel post successivo le immagini e i due disassemblati ...

Guglielmo

1 Like

Questa l'uscita dalla UNO R3 con l'utilizzo del framework Arduino:

e questa l'uscita dalla UNO R4 MINIMA con l'utilizzo del framework Arduino:

Questa volta riporto i disassemblati completi perché, come si vede, le varie funzioni Arduino vengono proprio richiamate nel codice ... esempio AVR:

#if defined (USE_FRAMEWORK)
#pragma message "Compiling using Arduino Framework"

pinMode (7, OUTPUT);
loop_frw:
digitalWrite (7, HIGH);
a84: 81 e0 ldi r24, 0x01 ; 1
a86: 0e 94 f1 00 call 0x1e2 ; 0x1e2 <digitalWrite.constprop.1>
digitalWrite (7, LOW);
a8a: 80 e0 ldi r24, 0x00 ; 0
a8c: 0e 94 f1 00 call 0x1e2 ; 0x1e2 <digitalWrite.constprop.1>
a90: f9 cf rjmp .-14 ; 0xa84 <main+0xbc>

... per cui, cercando nei disassemblati, si trova come sono scritte le funzioni sia per il pinMode() che per il digitalWrite():

Disassemblato AVR: AVR_FW.txt (67.6 KB)

Disassemblato R4: R4_FW.txt (795.6 KB)

A voi le dovute conclusioni ...

Guglielmo

P.S.: per disassemblare ho usato avr-objdump per AVR e arm-none-eabi-objdump per il Renesas ... li trovate entrambi nel Arduino IDE nei rispettivi packages.

2 Likes

Adesso capisci perché mi viene sempre voglia di usare PORT?... :grin:

No, lo capisco solo nei casi in cui c'è una reale necessità di velocità nel I/O ... in tutti gli altri casi è pessima cosa che rende intrasportabili i programmi.

Guglielmo