Faire clignoter une led !

On peut difficilement être plus proche du cœur du CPU qu'avec l'assembleur...

Modérateur : Francois

Répondre
Artemus24
Raspinaute
Messages : 803
Enregistré le : ven. 15 sept. 2017 19:15

Faire clignoter une led !

Message par Artemus24 » mer. 27 janv. 2021 08:50

Salut M. Vincent Leboulou.

Ma question cerne les accès aux GPIO de la raspberry.
Y-a-t-il une différence d'approche, s'il s'agit de la RPi 1A, RPi 1B, RPi 2B, RPi 3A,RPi 3B, RPi 3B+, RPi 4B ?

Cordialement.
Artemus24.
@+
--> RPI4B/8GB + Argon FanHAt
--> RPi0v1.3, RPi0W, Rpi3A+, Rpi3B+
--> Rpi0WH + Tuner TV HAT
--> RPi 2B + Joy-It I2C Serial 20x4 2004 LCD Module
--> RPi 2B + PIM273 Unicorn HAT HD 16x16 Leds RGB

VincentLeboulou
Messages : 34
Enregistré le : jeu. 19 oct. 2017 10:11

Re: Faire clignoter une led !

Message par VincentLeboulou » mer. 27 janv. 2021 14:58

re bonjour.
Au niveau de la programmation des fonctions du GPIO il n'y a pas de différence entre les modèles 1,2 et 3 , par contre l’adresse de base du GPIO est différente : 0x 3F003000 à la place de 0x20003000.
Pour rpi4, je l'ignore car je n'ai pas ce modèle !!
Je suis entrain de regarder la documentation du nouveau microcontroleur rpi pico car ne faisant pas appel à Linux, il doit être intéressant de programmer en assembleur surtout qu'il s'agit d'un processeur ARM 2 coeurs et donc beaucoup de concepts à découvrir.

Artemus24
Raspinaute
Messages : 803
Enregistré le : ven. 15 sept. 2017 19:15

Re: Faire clignoter une led !

Message par Artemus24 » mer. 27 janv. 2021 15:05

Salut M. Vincent Leboulou.

Grande question concernant les microcontrôleurs.
Je me suis dit qu'il serait intéressant d'utiliser des arduino.
Sauf qu'ils sont chers, et il y a tout un tas de HAT et autres composants à acheter pour réaliser un simple montage.
J'ai préféré me contenter de l'ESP32 qui répond plus à ma problématique, celle de la domotique.

Je ne vais pas me procurer, pour l'instant, un Raspberry Pico.
VincentLeboulou a écrit :par contre l’adresse de base du GPIO est différente : 0x 3F003000 à la place de 0x20003000.
Peux-tu préciser la raspberry qui est concerné par ce changement d'adresse ?

@+
--> RPI4B/8GB + Argon FanHAt
--> RPi0v1.3, RPi0W, Rpi3A+, Rpi3B+
--> Rpi0WH + Tuner TV HAT
--> RPi 2B + Joy-It I2C Serial 20x4 2004 LCD Module
--> RPi 2B + PIM273 Unicorn HAT HD 16x16 Leds RGB

Artemus24
Raspinaute
Messages : 803
Enregistré le : ven. 15 sept. 2017 19:15

Re: Faire clignoter une led !

Message par Artemus24 » mer. 3 févr. 2021 08:27

Salut à tous.

Je viens de terminer mon premier programme assembleur de type "Bare Metal".
J'ai fait mes tests sur la raspberry Pi Zero W.
En quoi consiste-t-il ? Rien de très extraordinaire, je fais clignoter deux leds :
--> celle que je dois installer sur une breadboard, reliée à la GPIO21 (Broche 40).
--> celle qui se nomme LED ACT, dont la couleur est verte, et qui indique l'activité de la raspberry. Elle se trouve sur la GPIO47.

Attention, ce programme ne fonctionne que sur la Raspberry Pi Zero W.

Quelques remarques avant de donner les sources :

1) voici ce dont j'ai besoin :
--> une nouvelle carte Micro SD genre celle de 4Go pour démarrer la RPi 2.
--> une raspberry Pi Zero W pour le test.
--> une breadboard.
--> des cavaliers mâle/femelle pour les jonctions.
--> une led rouge.
--> un transistor NPN.
--> une résistance de 1k ohms pour la led.
--> une résistance de 10k ohms pour le transistor.
--> prendre l'alimentation sur la Raspberry Pi Zero : GND (broche 6) et 3.3Vcc (broche 1).
--> branchement sur la GPIO 21 (broche 40).

Il n'est pas nécessaire d'utiliser un transistor.

2) dans le source assembleur, j'ai indiqué le type de processeur qui est utilisé :
--> Raspberry Pi Zero W : cortex-a7
--> Raspberry Pi 2B & 3B+ : cortex-a53
--> Raspberry Pi 4B : cortex-a72

Pour cela, il faut utiliser la pseudo instruction ".cpu" en début du programme.

3) Avant la partie "main", j'ai déclaré "bootstrap".
Cette partie sert à l'initialisation du processeur, ainsi que du programme avant son démarrage.
Dans cet exemple, j'initialise l'adresse de la pile (push & pop).

4) L'adresse de la base des GPIO est différente selon la version de la raspberry que vous utilisez.
--> RPi zero W : 0x20200000
--> RPi 2B & 3B+ : 0x3F200000
--> RPi 4B : 0xFE200000

C'est la même adresse pour les RPi 2B, 3A, 3B, 3A+ et 3B+.

5) il fait en premier sélectionner une GPIO.
Il y a cinquante-trois GPIO pour la Raspberry Pi Zero W.
Il faut trois bits pour sélectionner une GPIO.
Pour un registre de 32 bits, on ne peut gérer que 10 GPIO à la fois.
Soit au total six registres qui sont : GPSEL0, GPSEL1, GPSEL2, GPSEL3, GPSEL4 et GPSEL5.

Dans le bloc de trois bits, c'est le premier bit qui est positionné à 1 pour indiquer que la GPIO est en OUTPUT.
Voire la documentation sur le broadcom 2835.

6) Puis faire un ON ou un OFF de la GPIO.
Pour faire le ON, on sélectionne le registre GPSET et pour le OFF, on sélectionne le registre GPCLR.
Il faut seulement deux registres pour contenir nos cinquante-trois GPIO.
Le premier registre va gérer les 32 premiers GPIO, et le second, les 21 suivants.
Ils sont notés GPSET0 & GPSET1 pour le ON, et GPCLR0 & GPCLR1 pour le OFF.

7) à quoi correspond la LED ACT de couleur verte, selon le type de Raspberry utilisé ?
--> Pour la RPi A & B, c'est la GPIO16.
--> Pour la RPi A+ & B+, c'est la GPIO47.
--> Pour la RPi zero v1.3 & W, c'est aussi la GPIO47.
--> Pour la RPi 2B, c'est encore la GPIO47.
--> Pour la RPi 3B, ce n'est pas géré par les GPIO, mais par un extenseur du port I2C.
--> Pour la RPi 3A+ et 3B+, c'est la GPIO29.

Je n'ai testé que sur la Raspberry Pi zero W.

8) nous avons besoin d'un éditeur de liens pour créer le programme "Bare Metal".
Celui-ci va indiquer où mettre chaque section dans la mémoire.
Pour ce programme, j'utilise une section pour la pile (.stack) et une autre section pour le programme (.text).
En principe, j'ai aussi deux autres sections (.data & .bss) que je n'utilise pas ici.

9) j'ai aussi besoin du fichier makefile pour créer le fichier objet et l'exécutable.
Donc aupréalable, je dois installer l'outil pour faire l'assemblage en mode "Bare Metal" :

Code : Tout sélectionner

sudo apt-get install gcc-arm-none-eabi
10) j'ai besoin de ces trois outils :
--> AS = @arm-none-eabi-as
--> LD = @arm-none-eabi-ld
--> CPY = @arm-none-eabi-objcopy

11) sur la carte micro SD, vous devez recopier aupréalable les fichiers suivants :
--> bootcode.bin
--> start.elf
--> fixup.dat

Pour la raspberry Pi 4B, vous devez mettre ceux-ci ;
--> bootcode.bin
--> start4.elf
--> fixup4.dat

Pour le fichier exécutable, il doit se nommer :
--> kernel.img

Pour la raspberry Pi 4B, en 32 bits, il doit se nommer :
--> kernel71.img

12) je me suis créé un script bash qui réaliser la création de la carte micro SD, celle que je vais introduire dans la raspberry pi zero W.

@+
Modifié en dernier par Artemus24 le mer. 3 févr. 2021 10:52, modifié 1 fois.
--> RPI4B/8GB + Argon FanHAt
--> RPi0v1.3, RPi0W, Rpi3A+, Rpi3B+
--> Rpi0WH + Tuner TV HAT
--> RPi 2B + Joy-It I2C Serial 20x4 2004 LCD Module
--> RPi 2B + PIM273 Unicorn HAT HD 16x16 Leds RGB

Artemus24
Raspinaute
Messages : 803
Enregistré le : ven. 15 sept. 2017 19:15

Re: Faire clignoter une led !

Message par Artemus24 » mer. 3 févr. 2021 08:36

Salut à tous.

Et voici les fichiers :

1) le source assembleur :

Code : Tout sélectionner

@ ======================================================== @
@ Blink a led in GPIO21 (PIN40) & Led Green ACT in GPIO 47 @
@ -------------------------------------------------------- @
@         Raspberry PI Zero / BCM2835 (Cortex-a7)          @
@ ======================================================== @

        .cpu    cortex-a7               @ RPi zero W

@ ---------------- @
@ Storage Sections @
@ ---------------- @

        .section .data

        .section .bss

        .section .stack

@ ----------@
@ BootStrap @
@ --------- @

        .section .text

        .global  _start

_start: ldr     r0,=_stack_end_
        mov     sp,r0                   @ Address Stack Pointer

        b       main

        .ltorg

@ ---- @
@ Main @
@ ---- @

main:   ldr     r0,=0x20200000          @ GPIO Base for RPi Zero W

        mov     r1,#1
        lsl     r1,#21                  @ 7 x 3
        str     r1,[r0,#0x10]           @ GPFSEL4 : GPIO 47

        mov     r1,#1
        lsl     r1,#3                   @ 1 x 3
        str     r1,[r0,#0x08]           @ GPFSEL2 : GPIO 21

        mov     r1,#1
        lsl     r1,#15                  @ Offset 1 for GPIO 47 (47-32=15)

        mov     r2,#1
        lsl     r2,#21                  @ Offset 0 for GPIO 21

loop:   str     r1,[r0,#0x2C]           @ GPCLR1 : GPIO 47
        str     r2,[r0,#0x28]           @ GPCLR0 : GPIO 21
        bl      delay                   @ To put on hold

        str     r1,[r0,#0x20]           @ GPSET1 : GPIO 47
        str     r2,[r0,#0x1C]           @ GPSET0 : GPIO 21
        bl      delay                   @ To put on hold

        b       loop                    @ Loop Infinity

        .ltorg

@ ------------------ @
@ Subroutine : delay @
@ ------------------ @

delay:  push    {r0,lr}

        ldr     r0,=0x00280000          @ Counter

delay1: sub     r0,r0,#1
        cmp     r0,#0
        bgt     delay1

        pop     {r0,lr}
        bx      lr

        .ltorg
2) le fichier makefile :

Code : Tout sélectionner

# ----------------------------- #
#   Déclaration des Commandes   #
# ----------------------------- #

RM      = @rm -rf
CPY     = @arm-none-eabi-objcopy
LD      = @arm-none-eabi-ld
AS      = @arm-none-eabi-as
EX      = @chmod +x

# -------------------------- #
#   Déclaration des Objets   #
# -------------------------- #

SRC     = main.asm
OBJ     = main.o
ELF     = kernel.elf
BIN     = kernel.img

LNK     = linker.ld

# ------------------------------------ #
#   Compilation et Edition des liens   #
# ------------------------------------ #

all: clean $(BIN) after

.PHONY: clean

clean:
        clear
        $(RM) $(OBJ)
        $(RM) $(ELF)
        $(RM) $(BIN)

$(BIN): $(ELF)
        $(CPY) $(ELF) -O binary $(BIN)

$(ELF): $(OBJ)
        $(LD)  $(OBJ) -T $(LNK) -o $(ELF)

$(OBJ): $(SRC)
        $(AS)  $(SRC)           -o $(OBJ)

after:
        $(RM) $(OBJ)
        $(EX) $(BIN)

        objdump -d $(ELF)

        $(RM) $(ELF)
3) voici le résultat après avoir fait "make" :

Code : Tout sélectionner

objdump -d kernel.elf

kernel.elf:     format de fichier elf32-littlearm


Déassemblage de la section .text :

00000100 <_start>:
 100:   e59f0004        ldr     r0, [pc, #4]    ; 10c <_stack_end_+0xc>
 104:   e1a0d000        mov     sp, r0
 108:   ea000000        b       110 <main>
 10c:   00000100        .word   0x00000100

00000110 <main>:
 110:   e59f0040        ldr     r0, [pc, #64]   ; 158 <loop+0x1c>
 114:   e3a01001        mov     r1, #1
 118:   e1a01a81        lsl     r1, r1, #21
 11c:   e5801010        str     r1, [r0, #16]
 120:   e3a01001        mov     r1, #1
 124:   e1a01181        lsl     r1, r1, #3
 128:   e5801008        str     r1, [r0, #8]
 12c:   e3a01001        mov     r1, #1
 130:   e1a01781        lsl     r1, r1, #15
 134:   e3a02001        mov     r2, #1
 138:   e1a02a82        lsl     r2, r2, #21

0000013c <loop>:
 13c:   e580102c        str     r1, [r0, #44]   ; 0x2c
 140:   e5802028        str     r2, [r0, #40]   ; 0x28
 144:   eb000004        bl      15c <delay>
 148:   e5801020        str     r1, [r0, #32]
 14c:   e580201c        str     r2, [r0, #28]
 150:   eb000001        bl      15c <delay>
 154:   eafffff8        b       13c <loop>
 158:   20200000        .word   0x20200000

0000015c <delay>:
 15c:   e92d4001        push    {r0, lr}
 160:   e3a0070a        mov     r0, #2621440    ; 0x280000

00000164 <delay1>:
 164:   e2400001        sub     r0, r0, #1
 168:   e3500000        cmp     r0, #0
 16c:   cafffffc        bgt     164 <delay1>
 170:   e8bd4001        pop     {r0, lr}
 174:   e12fff1e        bx      lr
>
C'est le désassemblage du programme !
On ne dit pas déassemblage (il manque le s) mais bien désassemblage.

3) le fichier "linker.ld" :

Code : Tout sélectionner

ENTRY(_start)

MEMORY
{
/*      mem1 (rwx) : ORIGIN = 0x0000, LENGTH = 0x0100   */
        mem2 (rwx) : ORIGIN = 0x0000, LENGTH = 0x0100
        mem3 (rx)  : ORIGIN = 0x0100, LENGTH = 0x0100
}

SECTIONS
{
/*      .data  : { *(.data)            } > mem1
        .bss   : { *(.bss)             } > mem1         */
        .stack : { _stack_start_ = .;
                   . += 0x0100;
                   _stack_end_   = .;  } > mem2
        .text  : { *(.text) *(.rodata) } > mem3
}
4) le script bash qui va créer ma carte micro SD :

Code : Tout sélectionner

#!/bin/bash

UUID="2D91-464A"

clear
echo -e "\t\t*==============================*"
echo -e "\t\t*                              *"
echo -e "\t\t*     Recopie \"Kernel.img\"     *"
echo -e "\t\t*                              *"
echo -e "\t\t*==============================*\n"
echo -e "\t\tUUID\t\t: $UUID\n"

# ======================================== #
# Vérification l'existence de la partition #
# ======================================== #

if [ -L "/dev/disk/by-uuid/$UUID" ]
then
        DEVICE="$(blkid -U $UUID)"
        DIRECTORY="$(lsblk -n -o MOUNTPOINT $DEVICE)"

        echo -e "\t\tRépertoire\t: $DIRECTORY"
        echo -e "\t\tPériphérique\t: $DEVICE\n"

        if [ "$DIRECTORY" == "/mnt/boot" ]
        then
                echo -e "\t\t\e[31mPartition déjà montée\e[0m\n"
        else
                if [ "$DIRECTORY" != "" ]
                then
                        echo -e "\t\t\e[32mPartition a démonter !\e[0m"

                        umount $DEVICE

                        if [[ $? == 0 ]]
                        then
                                echo -e "\t\t\e[32mPartition démontée\e[0m"
                        else
                                echo -e "\t\t\e[31mPartition non démontée\e[0m\n"
                                exit 3
                        fi
                fi

                mount -t vfat $DEVICE /mnt/boot

                if [[ $? == 0 ]]
                then
                        echo -e "\t\t\e[32mPartition a été montée\e[0m\n"
                else
                        echo -e "\t\t\e[31mPartition non montée\e[0m\n"
                        exit 2
                fi

# =========================== #
# Recopie des fichiers Kernel #
# =========================== #

                cd /mnt/boot

                rm -r *

                cp /boot/bootcode.bin                                   .

                cp /boot/start.elf                                      .
                cp /boot/start4.elf                                     .

                cp /boot/fixup.dat                                      .
                cp /boot/fixup4.dat                                     .

                cp /warehouse/Prog_Asm/04.Bare_Metal/kernel.img         .
                cp /warehouse/Prog_Asm/04.Bare_Metal/kernel.img         kernel71.img

                sync

                cd

                echo -e "\t\t\e[32mCopie effectuée !\e[0m\n"
        fi

# ========================= #
# Démontage de la partition #
# ========================= #

        echo -e "\t\t\e[32mPartition démontée\e[0m\n"

        umount $DEVICE
else
        echo -e "\t\t\e[32mLa carte Micro SD \"$UUID\" (carte N°3) n'est pas installée !\e[0m\n"
        exit 1
fi

exit 0
Il est bien plus simple de lancer ce script, que de le faire à chaque fois à la main.

Et c'est tout !

Dernière remarque concerant l'exécution du programme "Bare Metal".
Je n'ai pas compris pourquoi la rapidité du clignotement est plus lente avec les autres raspberry.
En fait, c'est la RPi Zero W qui est la plus rapide avec une vitesse du processeur qui est le plus lent.

J'espère que cela va servir à quelqu'un !

@+
--> RPI4B/8GB + Argon FanHAt
--> RPi0v1.3, RPi0W, Rpi3A+, Rpi3B+
--> Rpi0WH + Tuner TV HAT
--> RPi 2B + Joy-It I2C Serial 20x4 2004 LCD Module
--> RPi 2B + PIM273 Unicorn HAT HD 16x16 Leds RGB

Répondre

Retourner vers « Assembleur »