jeudi 19 novembre 2015

Echo sur Arduino nano sans IDE

En utilisant le strict minimum, à partir de la datasheet de l'ATmega368 :
#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>
/*
** UART
*/
void uart_init(void)
    {
    UBRR0H = 0x00;
    UBRR0L = 103;  /* F_CPU/16/baud-1 for 9600 bps*/
    UCSR0C = (1<<USBS0) | (3<<UCSZ00); /* 8N1 */
    UCSR0B = (1<<RXEN0) | (1<<TXEN0); /* enable */
    }
char my_getc()
    {
    while (!(UCSR0A & (1<<RXC0)))
        ;
    return(UDR0);  /* should handle errors */
    }
void my_putc(char c)
    {
    while (!(UCSR0A & (1<<UDRE0)))
        ;
    UDR0 = c;
    }
/*-----------------------------------------------*/
int main(void)
    {
    uart_init();

    for (;;)
        my_putc(my_getc());
    }
Avec comme commandes :
$ avr-gcc  -mmcu=atmega328 -Os -o echo.elf echo.c
$ avrdude -c arduino -p atmega328P -P /dev/ttyUSB0 -b 57600 -U flash:w:echo.elf
On peut tester avec
$ minicom -D /dev/ttyUSB0 -b 9600

AVR libc

Intrigué par le fait que mes putc() et getc() entraient en conflit avec des 'built-in', j'ai fini par me rendre compte de l'existence d'une véritable libc pour AVR... Et c'est quand même vachement mieux fichu que l'API Arduino. Ouf! Notamment du côté de stdio. Adapté, le programme devient quelque chose comme :
#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>
#include <stdio.h>
#include <string.h>
/*
** UART
*/
void uart_init(void)
    {
    UBRR0H = 0x00;
    UBRR0L = 103;  /* 9600 bps | F_CPU/16/baud-1 */
    UCSR0C = (1<<USBS0) | (3<<UCSZ00); /* 8N1 */
    UCSR0B = (1<<RXEN0) | (1<<TXEN0);
    }
int uart_getchar(FILE *stream)
    {
    while (!(UCSR0A & (1<<RXC0)))
        ;
    return(UDR0);
    }
int uart_putchar(char c, FILE *stream)
    {
    while (!(UCSR0A & (1<<UDRE0)))
        ;
    UDR0 = c;
    }
/*-----------------------------------------------*/
/* line input with minimal editing */
char *getline(char *cp, int n)
    {
    int    i=0;
    char    c;

    for(;;)
        {
        c = fgetc(stdin);
        if (c=='\n' || c=='\r' || i==(n-1))
            {
            cp[i] = '\0';
            return(cp);
            }
        if (c=='\b' && i>0)
            {
            fputc(c, stdout);
            fputc(' ', stdout);
            i -= 1;
            }
        else    cp[i++] = c;
        fputc(c, stdout);
        }
    }
int main(void)
    {
    char buf[32];

    uart_init();
    fdevopen(uart_putchar, uart_getchar);
    printf("Hello World\n\r");
    for (;;)
        {
        printf("\n\rReady> ");
        getline(buf, sizeof(buf));
        printf("\n\rstrlen('%s') is %d.\n\r", buf, strlen(buf));
        }
    }

lundi 6 juillet 2015

Digispark - ATTiny85

Digistump propose un petit gadget USB avec un AVR ATtiny85 directement relié au bus USB, le Digispark. Le micro-contrôleur se charge d'interpréter les signaux USB-1 (Étant OpenSource, on en trouve des clones).

Dans la même veine que l'article précédent, voici comment le programmer simplement.

Sur Ubuntu, il faut probablement les pré-requis suivants :
$ sudo apt-get install gcc-avr binutils-avr avr-libc
$ git clone https://github.com/micronucleus/micronucleus.git
$ ...
(en fait, je ne suis pas tout-à-fait sûr parce que j'ai aussi fait un 'apt-get install arduino'...)
Ensuite, le même petit programme 'C' :
#include <avr/io.h>
#define F_CPU 16000000UL
#include <util/delay.h>

int main(void)
    {
    DDRB  |= _BV(PB1);

    for (;;)
        {
        _delay_ms(1000);
        PORTB ^= _BV(PB1);
        }
    }
Il ne reste plus qu'à préparer le fichier .hex et le télécharger avec 'micronucleus' (micronucleus comporte deux parties : le firmware installé sur le chip en fin de FLASH (qui utilise V-USB) et la commande 'micronucleus' qui tourne sous Linux et utilise libusb).
$ avr-gcc  -mmcu=attiny85 -Os -o blink.elf blink.c
$ avr-objcopy -j .text -j .data -O ihex blink.elf blink.hex
$ micronucleus --run blink.hex
Il faut en fait lancer la commande 'micronucleus' puis introduire le bidule dans un port USB. Le flashage s'effectue et le programme commence à s'exécuter. La LED du Digispark (connectée à PB1) se met à clignoter (j'avais utilisé PB3 mais c'est probablement une mauvaise idée parce le port est utilisé pour communiquer sur l'USB). Notez que F_CPU est à 16 MHz. Cette vitesse est tirée de l'USB par le firmware micronucleus.

Le programme est compilé, par défaut, pour s'exécuter en début de FLASH. Le bootloader (firmware micronucleus) se situe en fin de FLASH; il s'exécute lors d'un 'power-on-reset', c'est lui qui flashe le programme au bon endroit et qui lui transfère l'exécution après cinq secondes. Il est conçu en sorte de ne pas s'écrire dessus, il n'y a donc aucun risque de 'briquer' le Digispark.

Maintenant, comment faire pour créer un firmware communiquant en USB? Soit partir de la documentation V-USB et/ou partir d'un firmware qui fonctionne comme Little Wire (ou une des nombreuses applications signalées sur le site de V-USB).

jeudi 7 mai 2015

ATTiny13 blink

Le but est de faire clignoter une LED avec un nombre réduit de composants, un environnement réduit et un programme simple... Dans ce cas-ci, le développement a lieu sur Ubuntu 14.04 mais cela devrait pouvoir se faire sur n'importe quel Linux, Windows,...

Arduino, c'est magique. Un peu trop magique... C'est quand même mieux de savoir ce que l'on fait et de s'approcher de la réalité des choses. Et donc, par exemple, d'utiliser un programmeur ISP (qui utilise un bus SPI...) pour programmer l'AVR directement plutôt que de passer par le bootloader installé dans l'Arduino. Au niveau du prix, quand on veut le faire soi-même, cela revient à peu près au même. Un clone de USBasp revient au même prix qu'une interface USB-série, quelque chose comme 2 euros. En plus, on n'est plus limité à l'ATMega368 (ou 168), on peut utiliser toute la gamme AVR, comme ici avec l'ATTiny13. On peut aussi se servir d'un Arduino comme programmateur avec ArduinoISP.



Il y a donc quelques fils à connecter : le 5 (ou 3?) volts (VCC), la terre (GND), les trois fils du SPI (MOSI, MISO, CLK) et le 'chip select' (c.-à-d. /RESET).

Le programme pour faire clignoter une LED, sans utiliser l'API Arduino (mais avr-libc), se réduit à
#include <avr/io.h>
#include <util/delay.h>

int main(void)
    {
    DDRB  |= _BV(PB3);
 
    for (;;)
        {
        _delay_ms(200);
        PORTB ^= _BV(PB3);
        }
    }
On programme le port3 en sortie et on joue sur le bit data correspondant pour faire clignoter la LED. (Notons que le compilateur va se plaindre que F_CPU n'est pas défini dans delay.h (il prend alors la valeur par défaut, 1 MHz); on pourrait ajouter un #define F_CPU 1000000UL avant l'include ou passer F_CPU au compilateur dans le Makefile)

Le Makefile, sans bell ni whistle, comprend 3 étapes : compiler avec avr-gcc, transformer le binaire obtenu en fichier texte (iHEX) et le téléverser avec avrdude. (On utilise les paquets gcc-avr, binutils-avr, avr-libc, avrdude; la plupart sont déjà requis par le paquet arduino)

blink.elf: blink.c
        avr-gcc  -std=c99 -Wall -Os -mmcu=attiny13 -o blink.elf blink.c

blink.hex: blink.elf
        avr-objcopy -j .text -j .data -O ihex blink.elf blink.hex

flash: blink.hex
        avrdude -c usbasp-clone -p t13 -U flash:w:blink.hex

Pour peu que l'ont ait créé un /etc/udev/rules.d/99-USBasp.rules avec

SUBSYSTEM=="usb", ATTR{product}=="USBasp", ATTR{idProduct}=="05dc", ATTRS{idVendor}=="16c0", MODE="0666"

dedans, on peut simplement faire
$ make flash
et obtenir une LED qui clignote sur PB3.



Une fois enlevé le cordon ombilical de programmation, il ne reste que l'alimentation, le microcontrôleur, une résistance de 10K entre /RESET et VCC, un condensateur de 100nF entre VCC et GND et une 'blinkenlight' entre PB3 et GND (dans ce cas-ci une LED et une résistance de 1K (c'est beaucoup mais selon AVR Programming, comme ça cela fonctionne aussi avec 12 volts).