Bus de communication série en bare metal : registres SPI (SPCR, SPDR) et TWI (TWBR, TWCR, TWDR)
L'ATmega328P intègre un module SPI matériel complet. Les broches sont fixées par le matériel :
| Signal | AVR Port | Arduino Pin |
|---|---|---|
| SCK (horloge) | PB5 | D13 |
| MOSI (données sortantes) | PB3 | D11 |
| MISO (données entrantes) | PB4 | D12 |
| SS (chip select) | PB2 | D10 |
#include <avr/io.h>
#define CS_PIN PB2
#define CS_LOW PORTB &= ~(1 << CS_PIN)
#define CS_HIGH PORTB |= (1 << CS_PIN)
void spi_init(void) {
// MOSI, SCK, SS en sortie
DDRB |= (1 << PB3) | (1 << PB5) | (1 << CS_PIN);
// MISO en entrée (déjà par défaut)
CS_HIGH; // CS inactif
// SPI Enable, Master, Mode 0, Fosc/16
SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR0);
}
uint8_t spi_transfer(uint8_t data) {
SPDR = data; // Lancer le transfert
while (!(SPSR & (1 << SPIF))); // Attendre fin
return SPDR; // Lire donnée reçue
}
// Utilisation :
// CS_LOW;
// uint8_t reponse = spi_transfer(0x42);
// CS_HIGH;
| SPR1:SPR0 | SPI2X | Diviseur | Fréq. SCK |
|---|---|---|---|
| 00 | 1 | /2 | 8 MHz |
| 00 | 0 | /4 | 4 MHz |
| 01 | 0 | /16 | 1 MHz |
| 10 | 0 | /64 | 250 kHz |
| 11 | 0 | /128 | 125 kHz |
Sur les AVR, le module I2C s'appelle TWI (Two-Wire Interface). Il est fonctionnellement identique à l'I2C. Les broches sont :
N'oubliez pas les résistances pull-up de 4.7 kΩ sur SDA et SCL !
| Registre | Rôle |
|---|---|
TWBR | Bit Rate Register — définit la vitesse SCL |
TWCR | Control Register — Start, Stop, ACK, Enable, Interrupt |
TWSR | Status Register — code d'état de la dernière opération |
TWDR | Data Register — données à envoyer ou reçues |
#include <avr/io.h>
/* ---- Initialisation TWI à 100 kHz ---- */
void twi_init(void) {
TWBR = 72; // 100 kHz avec F_CPU=16MHz, prescaler=1
TWSR = 0x00; // Prescaler = 1
TWCR = (1 << TWEN); // Activer TWI
}
/* ---- Start ---- */
void twi_start(void) {
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
}
/* ---- Stop ---- */
void twi_stop(void) {
TWCR = (1<<TWINT) | (1<<TWSTO) | (1<<TWEN);
}
/* ---- Envoyer un octet ---- */
void twi_write(uint8_t data) {
TWDR = data;
TWCR = (1<<TWINT) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
}
/* ---- Recevoir avec ACK ---- */
uint8_t twi_read_ack(void) {
TWCR = (1<<TWINT) | (1<<TWEN) | (1<<TWEA);
while (!(TWCR & (1<<TWINT)));
return TWDR;
}
/* ---- Recevoir avec NACK (dernier octet) ---- */
uint8_t twi_read_nack(void) {
TWCR = (1<<TWINT) | (1<<TWEN);
while (!(TWCR & (1<<TWINT)));
return TWDR;
}
/* ---- Exemple : lire 1 octet d'un capteur ---- */
uint8_t i2c_read_register(uint8_t addr, uint8_t reg) {
twi_start();
twi_write((addr << 1) | 0); // Write
twi_write(reg);
twi_start(); // Repeated Start
twi_write((addr << 1) | 1); // Read
uint8_t data = twi_read_nack();
twi_stop();
return data;
}
| Arduino | Bare metal AVR |
|---|---|
Wire.begin() | twi_init() |
Wire.beginTransmission(addr) | twi_start(); twi_write(addr<<1); |
Wire.write(data) | twi_write(data) |
Wire.endTransmission() | twi_stop() |
SPI.begin() | spi_init() |
SPI.transfer(data) | spi_transfer(data) |
Le code bare metal est très proche de l'API Arduino mais vous donne le contrôle complet sur les vitesses, les modes, les codes d'état TWI et la gestion d'erreurs — ce que les bibliothèques Arduino masquent.