🛠 Manipulation directe des registres PORT : le bit-banging à 16 MHz
Objetivos
digitalWrite es práctico pero lento en un AVR de 8 bits: una decena de ciclos por llamada, lo que limita la frecuencia a unas pocas decenas de kHz. Para controlar neopíxeles, una pantalla multiplexada o un protocolo propio, hay que bajar al nivel de los registros: DDRx, PORTx, PINx. La tabla oficial de correspondencia pines Arduino ↔ bits de puerto (PinMapping168) es su carta de navegación. Va a generar una señal medible con osciloscopio y escribir en un puerto completo de una sola vez.
Material
- 1 Arduino Duemilanove (ATmega328)
- 1 osciloscopio o analizador lógico (si no, un contador de frecuencia en el pin)
- No se requiere cableado: el LED del pin 13 sirve como carga visible
Código completo
// Carre rapide sur la broche 13 = PORTB bit 5 (voir PinMapping168)
// Broches 8..13 = PORTB bits 0..5 ; broches 0..7 = PORTD bits 0..7 ;
// broches analogiques A0..A5 = PORTC bits 0..5.
void setup {
DDRB |= (1 << DDB5); // broche 13 en sortie (registre DDR du port B)
// Equivalence : pinMode(13, OUTPUT);
}
void loop {
noInterrupts; // le timer0 du millis perturbe les mesures
PORTB |= (1 << PORTB5); // broche 13 a 1 -> 1 cycle CPU
PORTB &= ~(1 << PORTB5); // broche 13 a 0 -> 1 cycle CPU
// NB : |= et &= font 2 ecritures ; en assembleur, sbi/cbi font 1 cycle.
interrupts;
}
Escribir 8 LEDs de una vez: PORTD = B10101010; coloca los pines 0–7 en el estado deseado con una sola instrucción (atención: los pines 0/1 son la conexión serie FTDI — no rompa el monitor mientras se está cargando el programa).
Verificación esperada
Con el osciloscopio en el pin 13, una señal cuyo período se cuenta en cientos de nanosegundos (los bucles en C rondan los ~1–2 MHz de conmutación, mucho más con sbi/cbi en ensamblador) — frente a ~50 kHz máximo con digitalWrite. El LED 13 permanece débilmente encendido (PWM natural por relación de trabajo). La prueba «una escritura, 8 LEDs» hace cambiar todo un puerto simultáneamente.
Errores frecuentes
- Escribir en PORTD sin pensar: los bits 0 y 1 son RX/TX hacia la FTDI; un
PORTD = 0involuntario rompe el monitor serie y puede dificultar la carga del programa. - Olvidar DDRx: escribir en PORTx sin haber configurado el pin como salida no controla nada (o solo activa la resistencia pull-up interna, PORTx=1 en modo entrada).
- Confundir PORTx y PINx: leer el estado real de los pines se hace a través de PINx; leer PORTx vuelve a leer la consigna de salida.
- Modificar un puerto compartido mediante lectura-modificación-escritura en una interrupción: riesgo de condición de carrera con el código principal; usar bits atómicos (sbi/cbi) o desactivar las interrupciones.
- Olvidar que millis funciona mediante el timer0: las interrupciones de 1 ms añaden jitter a las mediciones de frecuencia — de ahí
noInterruptsdurante la medición. - Usar los registros en un chip 168 creyendo que los pines son los mismos: la correspondencia es idéntica para los pines estándar, pero la SRAM (1 KB) y la flash (14 336 bytes) limitan rápidamente los grandes arrays de tablas.
- Verificar con un multímetro: a estas frecuencias muestra una tensión media engañosa — se necesita el osciloscopio.
Otros tutoriales para esta placa
- Faire clignoter la LED 13 d'une Duemilanove (et choisir le bon ATmega)
- Bouton poussoir et moniteur série sur la Duemilanove (avec ses 0,5 s de silence)
- Potentiomètre et variation LED : l'ADC 10 bits et le PWM 8 bits version AVR
- Ajouter un deuxième port série avec SoftwareSerial (GPS sur broches 10/11)
- Régler la référence de mesure avec AREF et analogReference
- Persister une calibration dans l'EEPROM de l'ATmega (512 octets ou 1 Ko)
Ejemplos de código para esta placa
clignotante.inoArduinobouton-led.inoArduinopwm-respiration.inoArduinopotentiometre-plotter.inoArduinoservo-balayage.inoArduinobuzzer-melodie.inoArduino