Vous êtes sur la page 1sur 11

Communication MCU-MPU sur le STM32MP157

7 octobre 2020

L’objectif de ce document est d’expliquer comment mettre en œuvre le Cortex A7 sous GNU/Linux
et d’en programmer le Cortex M4 du STM32MP1 afin de les faire communiquer.
L’image GNU/Linux s’exécutant sur le Cortex-A7 du STM32MP1 est issue de Buildroot, dont la
chaı̂ne de compilation croisée est obtenue par
make stm32mp157_dk_defconfig puisque T. Petazzoni de Bootlin 1 a inclus la configuration de cette
plateforme dans Buildroot. Cependant, par rapport à cette documentation qui propose d’utiliser une
branche séparée maintenant (Nov. 2019) obsolète, nous restons sur la branche master de Buildroot (git
checkout master) et configurons
make stm32mp157c_dk2_defconfig 2 Par rapport à la configuration par défaut, nous ajoutons le support
pour WCHAR de la chaı̂ne de compilation croisée ainsi que le serveur ssh dropbear (make linux-menuconfig)
ainsi que éventuellement screen, et vérifions le support de IIO dans le noyau (make linux-menuconfig).
La communication avec le STM32MP157 se fait initialement par le port série virtuel créé sur le
connecteur USB-microB en /dev/ttyACM0. Aprés avoir configuré l’IP statique de l’interface Ethernet
dans /etc/network/interfaces :
auto eth0
iface eth0 inet static
address 192.168.1.30
netmask 255.255.255.0
gateway 192.168.1.1
et ajouté un mot de passe (passwd) à root, la connexion par ssh est fonctionnelle (notamment pour
copier des fichiers par scp).
Nous avons initialement tenté de compiler un programme trivial pour Cortex-M4 et le transférer
depuis le A7 (description de la procédure ci-dessous) pour constater qu’il manque au binaire ELF une
partie de l’entête que sont la table des ressources. N’ayant pas identifié de façon simple d’ajouter cet
entête, et ayant constaté que nombre de projets exploitant le mode de communication A7-M4 (ou MPU-
MCU dans la nomenclature ST) nécessite un environnement exécutif temps-réel, nous nous proposerons
d’exécuter Zephyr sur le M4.

1 Compilation de Zephyr
La nouvelle mode 3 semble être de placer les exécutables compilés par l’utilisateur dans un répertoire
.local. Nous commençons donc par export PATH=$PATH:$HOME/.local/bin
L’installation de Zephyr est très bien décrite sur leur site : après avoir installé leur outil west (il
serait souhaitable de s’affranchir de cette étape en git clonant à la main), nous complétons l’installation
de Zephyr par

west init # clone git


cd zephyr
west update

Comme nous ne voulons pas encore installer une toolchain de plus puisque notre classique arm-none-eabi-gcc
fonctionne très bien, nous complétons dans le répertoire zephyr la liste des variables d’environnement
de zephyr-env.sh par

export ZEPHYR_TOOLCHAIN_VARIANT=cross-compile
export CROSS_COMPILE=/usr/bin/arm-none-eabi-
1. https://bootlin.com/blog/building-a-linux-system-for-the-stm32mp1-basic-system/
2. modification du 27 octobre 2019 :“configs/stm32mp157 dk : rename to the sm32mp157c dk2”
3. https://docs.zephyrproject.org/latest/getting_started/index.html indique que pip place ses binaires dans ce
répertoire

1
puis nous sourçons ce fichier pour charger les variables d’environnement (à refaire évidemment chaque fois
que nous utiliserons Zephyr dans un nouveau terminal). Dans notre cas, nous utilisons arm-none-eabi-gcc
des paquets Debian/GNU Linux gcc-arm-none-eabi et binutils-arm-none-eabi. À titre d’informa-
tion, ZEPHYR TOOLCHAIN VARIANT doit être une des options parmi celles décrites dans
zephyr/cmake/toolchain/gnuarmemb/.
Nous allons compiler notre premier exemple pour vérifier le bon fonctionnement de la toolchain par
(en partant du répertoire zephyr) :
cd samples/basic/blinky/
mkdir build
cd build
cmake -DBOARD=olimexino_stm32 ../ # commencons simple par une STM32F103
make
cd zephyr
arm-none-eabi-objcopy -O binary zephyr.elf zephyr.bin
stm32flash.sh -g 0x0 -w zephyr.bin /dev/ttyUSB0 # tester sur un board contenant le STM32F103
L’utilisateur possédant une plateforme contenant un STM32F103 pourra valider le bon fonctionne-
ment du code de clignotement de LED. Pour donner une idée de la taille du binaire, cet exemple trivial
occupe 12028 octets.
Bien que cet exemple ne fonctionne pas dans l’émulateur qemu porté au STM32F103 par André
Beckus, Zephyr propose sa propre version de l’émulateur, qui n’est cependant pas très verbeux sur les
accès aux périphériques autres que le port série de communication.
Ainsi, par cmake -DBOARD=qemu cortex m3 dans le répertoire samples/hello world de zephyr nous
obtenons un binaire pour l’émulateur qemu fourni par Zephyr qui répond bien (make run) par
To exit from QEMU enter: ’CTRL+a, x’
[QEMU] CPU: cortex-m3
qemu-system-arm: warning: nic stellaris_enet.0 has no peer
***** Booting Zephyr OS build zephyr-v2.0.0 *****
Hello World! qemu_cortex_m3
QEMU: Terminated
[100%] Built target run
Ce même exemple, hello world, fonctionne sur STM32F4-discovery et affiche sur la broche PA2
(USART TX)
***** Booting Zephyr OS build zephyr-v2.0.0 *****
Hello World! stm32f4_disco
si on compile par cmake -DBOARD=stm32f4\_disco ../ && make && make flash
Maintenant que nous sommes convaincus du bon fonctionnement de Zephyr sur STM32Fx, nous
pouvons sereinement aborder le STM32MP157. Afin de faire clignoter la LED orange connectée sur la
broche 7 du port H 4 , nous considérons le programme blinky modifié de la façon suivante
#i n c l u d e <z e p h y r . h>
#i n c l u d e <d e v i c e . h>
#i n c l u d e <d r i v e r s / g p i o . h>
#d e f i n e LED PORT " GPIOH "
#d e f i n e LED 7
#d e f i n e SLEEP TIME 1000

v o i d main ( v o i d )
{
u32 t cnt = 0 ;
s t r u c t d e v i c e ∗ dev ;
dev = d e v i c e g e t b i n d i n g (LED PORT) ;
g p i o p i n c o n f i g u r e ( dev , LED, GPIO DIR OUT) ;

4. nous aurions du apprendre cette configuration de la lecture de https://www.st.com/content/ccc/resource/


technical/document/user_manual/group1/d6/59/df/e0/8f/e7/45/8f/DM00591354/files/DM00591354.pdf, Table 13 page
19, mais en pratique c’est en consultant https://visualgdb.com/tutorials/arm/stm32/stm32mp1/ que nous avons vu où
est branchée la LED

2
while (1) {
g p i o p i n w r i t e ( dev , LED, c n t % 2 ) ;
c n t ++;
k s l e e p (SLEEP TIME) ;
}
}
Ce programme est compilé pour le STM32MP157 en configurant par cmake -DBOARD=stm32mp157c_dk2 ../.
Le binaire au format ELF généré dans le répertoire zephyr du répertoire de compilation est transféré
(scp après avoir installé dropbear dans Buildroot du MP157 et ajouté un mot de passe à root) dans le
répertoire /lib/firmware (à créer à son premier accès car il n’existe pas par défaut), puis 5

echo zephyr_LED_USART.elf > /sys/class/remoteproc/remoteproc0/firmware


echo start > /sys/class/remoteproc/remoteproc0/state

La LED doit clignoter, tel que le prouve jmfriedt.org/MOV_0298.mp4, après chargement du firmware
et dmesg indique

[ 511.590367] remoteproc remoteproc0: powering up m4


[ 511.598782] remoteproc remoteproc0: Booting fw image zephyr.elf, size 411464
[ 511.613100] rproc-srm-core m4@0:m4_system_resources:
bound m4@0:m4_system_resources:serial@4000f000 (ops 0xc075f54c)
[ 511.629459] remoteproc remoteproc0: remote processor m4 is now up

Le message lié à rproc-srm-core est obtenu en ajoutant au devicetree, dans


arch/arm/boot/dts/stm32mp157c-dk2.dts le nœud
&m4_rproc {
m4_system_resources {
status = "okay";
};
};
mais son utilité reste à démontrer.
Nous pouvons communiquer par LED mais comment obtenir le résultat d’une transaction sur le
port série ? Zephyr explique 6 avoir désactivé la communication par USART3 et communiquer par “The
Zephyr console output is assigned by default to the RAM console to be dumped by the Linux Remoteproc
Framework on Cortex-A7 core”. Il nous faut donc arriver à cette RAM console : pour ce faire, activer
Debug FS dans le noyau Linux de Buildroot, et une fois Debug FS installé :
mount -tdebugfs debugfs /sys/kernel/debug
cd /sys/kernel/debug/remoteproc/remoteproc0/
echo zephyr_LED_USART.elf > /sys/class/remoteproc/remoteproc0/firmware
echo start > /sys/class/remoteproc/remoteproc0/state
cat trace0

***** Booting Zephyr OS build zephyr-v2.0.0 *****


Hello World! stm32mp157c_dk2
Hello World! stm32mp157c_dk2
Hello World! stm32mp157c_dk2
Hello World! stm32mp157c_dk2
qui affichera bien le message transmis sur le F4 sur le port série.
5. https://wiki.st.com/stm32mpu/index.php/Linux_remoteproc_framework_overview
6. https://docs.zephyrproject.org/latest/boards/arm/stm32mp157c_dk2/doc/stm32mp157_dk2.html

3
2 Accès aux ADCs
ST Microelectronics fournit les pilotes IIO 7 pour accéder aux convertisseurs analogiques numériques
qui sont convenablement définis dans le devicetree 8 . Ainsi, la lecture des mesures se fait trivialement
par
# pwd
/sys/bus/iio/devices/iio:device0
# cat in_voltage0_raw
2704
Plus intéressant, la mesure en continu des échantillons s’obtient en jouant avec un timer pour
déclencher les conversions 9 :

echo 1 > /sys/bus/iio/devices/iio\:device1/scan_elements/in_voltage0_en


echo 1 > /sys/bus/iio/devices/iio\:device1/scan_elements/in_voltage1_en
cat /sys/bus/iio/devices/iio\:device1/trigger/current_trigger
cat /sys/bus/iio/devices/trigger0/name
echo tim6_trgo > /sys/bus/iio/devices/iio\:device1/trigger/current_trigger
cat /sys/bus/iio/devices/iio\:device1/trigger/current_trigger
echo 100 > /sys/bus/iio/devices/iio\:device1/buffer/length
cat /sys/bus/iio/devices/iio\:device1/buffer/length
echo 1 > /sys/bus/iio/devices/iio\:device1/buffer/enable
echo 100000 > /sys/bus/iio/devices/trigger0/sampling_frequency
cat < /dev/iio\:device1 | xxd

AIN0 et AIN1 sont sur ADC2, d’où l’utilisation de iio:device1. Ces broches sont compatibles sur
les deux broches du milieu (3 et 4) du “petit connecteur compatible Arduino”.
Le format des données issues du /dev/iio* est décrit à 10 comme une mesure interlacée des traces
actives (ici 2) suivi d’un horodatage.

3 Communication depuis M4 sur le port série USART3


Deux ports série sont routés depuis le M4 pour être accessibles : USART7 sur les broches compa-
tibles Arduino, et USART3 sur le connecteur 2 × 20-broches. Par défaut, activer la Console de Zephyr
sur USART désactive la commication par remoteproc et envoie les messages sur USART3. Cependant,
USART3 n’est pas actif dans la configuration par défaut de Zephyr.
Nous nous plaçons dans le répertoire samples/hello world de Zephyr. Router la console sur USART3
nécessite de
1. activer le port dans le fichier devicetree à zephyr/boards/arm/stm32mp157c dk2/*.dts en décommentant
les deux lignes zephyr,console = &usart3; et zephyr,shell-uart = &usart3;
2. en configurant la console comme USART par make menuconfig après avoir
cmake -DBOARD=stm32mp157c_dk2 ../ : rechercher (“/”) le terme CONSOLE et activer dans
CONSOLE(=y) "Console drivers" l’option [*] Use UART for console
3. toujours dans make menuconfig, activer l’USART3 en recherchant UART_3(=y) "Enable STM32 USART3 Port"
et en s’assurant que [*] Enable STM32 USART3 Port est actif
Une fois ces configurations conclues, make doit compiler le projet. En cas d’erreur sur un fichier
d’entête inexistant, nettoyer le répertoire de travail (west build -t clean) et reprendre toutes ces
étapes. En connectant un cable RS232-USB sur les broches USART3/TX (en partant du côté opposé
7. https://wiki.st.com/stm32mpu/wiki/ADC_Linux_driver
8. https://wiki.st.com/stm32mpu/wiki/ADC_device_tree_configuration
9. https://lkml.org/lkml/2019/3/15/520
10. https://www.linux4sam.org/bin/view/Linux4SAM/IioAdcDriver

4
Figure 1 – Connexion du port USART3 vers un convertisseur RS232-USB pour observer les trames
émises par le M4.

aux quatre connecteurs USB-A, i.e. du côté du port USB-C d’alimentation, il s’agit de la 3ème broche
la sur le bord de la carte pour la masse et de la quatrième pour USART3/TX : Fig. 3) 11
Le résultat, lors du chargement du programme suivant dans le M4,
#i n c l u d e <z e p h y r . h>
#i n c l u d e <s y s / p r i n t k . h>
#i n c l u d e <d e v i c e . h>
#i n c l u d e <d r i v e r s / g p i o . h>

#d e f i n e LED PORT " GPIOH "


#d e f i n e LED 7
#d e f i n e SLEEP TIME 1000

v o i d main ( v o i d )
{ i n t cnt = 0 ;
s t r u c t d e v i c e ∗ dev ;
dev = d e v i c e g e t b i n d i n g (LED PORT) ;
g p i o p i n c o n f i g u r e ( dev , LED, GPIO DIR OUT) ;
while (1) {
p r i n t k ( " Hello World ! % s \ n " , CONFIG BOARD) ;
g p i o p i n w r i t e ( dev , LED, c n t % 2 ) ;
c n t ++;
k s l e e p (SLEEP TIME) ;
}
}
doit être une LED qui clignote (indiquant que les horloges ont bien été configurées et donc que le
processeur n’a pas cessé son exécution lors de la configuration des périphériques) et l’affichage sur le port
série de
***** Booting Zephyr OS build zephyr-v2.0.0-1957-g2d4d6f05e60e *****
Hello World! stm32mp157c_dk2
Hello World! stm32mp157c_dk2
Hello World! stm32mp157c_dk2
Hello World! stm32mp157c_dk2

4 Communication MPU–MCU
La communication MPU–MCU ne semble pas (encore) officiellement supportée par Zephyr. Nous
devons donc nous appuyer sur la branche d’Arnaud Pouliquen à https://github.com/arnopo/. Pour
ce faire :
1. cloner le dépôt en passant par west qui se chargera ensuite de télécharger toutes les dépendances :
west init -m https://github.com/arnopo/zephyr_stm32mp1.git --mr OpenAMP_rsc_table_sample
11. voir page 10 du schéma de https://www.st.com/content/ccc/resource/technical/layouts_and_diagrams/
schematic_pack/group0/36/8e/ea/7a/ca/ca/4b/e4/mb1272-dk2-c01_schematic/files/MB1272-DK2-C01_Schematic.pdf
pour constater que le brochage de CN2 assigne EXP GPIO14 à la broche 8 tandis que 6 est à la masse, puisque
EXP GPIO14 et USART3 TX sur le schéma de gauche.

5
2. mettre à jour le dépôt
west update
3. effacer le répertoire de cache de Zephyr qui a été source de bien des déboirs
rm -rf $HOME/.cache/zephyr
4. analyser le code disponible dans
zephyr/samples/subsys/ipc/openamp_o_uart/src/remote et le compiler par
mkdir build && cd build && cmake -DBOARD=stm32mp157c_dk2 ../ && make
5. envoyer le fichier .elf résultant dans zephyr sur le STM32MP157 (répertoire /lib/firmware
6. télécharger le module noyau depuis
https://elixir.bootlin.com/linux/latest/source/samples/rpmsg/rpmsg_client_sample.c
(nous avons du copier-coller le code source, n’ayant pas trouvé de solution pour obtenir le code
source sans le formatage HTML !)
7. rédiger le Makefile pour compiler contre le noyau fourni par Buildroot pour le STM32MP157.
Dans notre cas, ce Makefile ressemble à
PATH:=$(PATH):/mnt/sdb2/stm32mp1/buildroot/output/host/usr/bin/

obj-m += rpmsg_client_sample.o

all:
make ARCH=arm CROSS_COMPILE=arm-buildroot-linux-uclibcgnueabihf- -C \
/mnt/sdb2/stm32mp1/buildroot/output/build/linux-custom/ M=$(PWD) modules

clean:
rm *.o *.ko *.mod.c .*.cmd
en supposant que Buildroot se trouve dans /mnt/sdb2/stm32mp1/buildroot/
Une fois ces étapes achevées, nous configurons le cœur M4 comme nous l’avons déjà vu par
echo zephyr_openamp_rsc_table.elf > /sys/class/remoteproc/remoteproc0/firmware
echo start > /sys/class/remoteproc/remoteproc0/state

qui doit se traduire par les messages suivants dans les logs du noyau (dmesg)
[ 105.197021] remoteproc remoteproc0: Booting fw image zephyr_openamp_rsc_table.elf, size 556960
[ 105.213691] rproc-srm-core m4@0:m4_system_resources: bound m4@0:m4_system_resources:serial@4000f00
[ 105.230464] m4@0#vdev0buffer: assigned reserved memory node vdev0buffer@10044000
[ 105.244212] virtio_rpmsg_bus virtio0: creating channel rpmsg-client-sample addr 0x0
[ 105.258162] virtio_rpmsg_bus virtio0: rpmsg host is online
[ 105.269473] m4@0#vdev0buffer: registered virtio0 (type 7)
[ 105.280667] remoteproc remoteproc0: remote processor m4 is now up
Une fois les canaux de communication créés, nous chargeons (insmode) le module noyau rpmsg_client_sample.ko
pour obtenir

[ 422.632959] rpmsg_client_sample virtio0.rpmsg-client-sample.-1.0: incoming msg 91 (src: 0x0)


[ 422.648051] rpmsg_sample_cb68 65 6c 6c 6f 20 77 6f 72 6c 64 21 hello world!
[ 422.648111] rpmsg_client_sample virtio0.rpmsg-client-sample.-1.0: incoming msg 92 (src: 0x0)
[ 422.663191] rpmsg_sample_cb68 65 6c 6c 6f 20 77 6f 72 6c 64 21 hello world!
...
[ 422.784416] rpmsg_client_sample virtio0.rpmsg-client-sample.-1.0: goodbye!
[ 422.797912] virtio_rpmsg_bus virtio0: destroying channel rpmsg-client-sample addr 0x0
[ 422.812494] rpmsg_client_sample virtio0.rpmsg-client-sample.-1.0: rpmsg sample client driver is re

qui prouve le bon fonctionnement de la communication entre le M4 et l’A7.

6
5 Accès à un périphérique matériel connecté sur bus SPI
Zephyr propose, pour le STM32MP157, le support du bus SPI, et en particulier SPI4 sur les broches
“compatibles Arduino”. Plusieurs octets peuvent être écrits ou lus séquentiellement sur le bus en activant
le signal de Chip Select (Slave Select) automatiquement : nous vérifions que ce signal ne se relève pas
entre chaque octet transmis/lu.
L’exercice porte sur le convertisseur analogique-numérique LTC1407A, sélectionné pour sa capacité
à être déclenché par un signal externe pour mémoriser la tension d’entrée avec une bande passante de
18 GHz. Malgré un débit modeste, ce convertisseur est approprié pour les mesures de RADAR sous
hypothèse de stationnarité de l’environnement pour une mesure stroboscopique.
La connexion se fait de la façon suivante :

Broche SPI Pin STM32MP157 Connecteur Arduino LTC1407


NSS PE11 D10 CONV
CLK PE12 D13 CLK
MISO PE13 D12 DO

Le programme permettant la mesure se résume à


#i n c l u d e <e r r n o . h>
#i n c l u d e <z e p h y r . h>
#i n c l u d e <s y s / p r i n t k . h>
#i n c l u d e <d e v i c e . h>
#i n c l u d e <d r i v e r s / s p i . h>
#i n c l u d e <d r i v e r s / g p i o . h>

void show array ( u8 t ∗ arr , u8 t a r r a y l e n )


{ u8 t inc ;
p r i n t k ( " %04 d \ n " , ( ( ( ( ( s i g n e d s h o r t ) a r r [ 2 ] ) &0x3F )<<8) | ( ( ( s i g n e d s h o r t ) a r r [ 3 ] ) &0xFF ) )>>2)→
,→ ;
}

i n t m y s p i r e a d ( s t r u c t d e v i c e ∗my dev , s t r u c t s p i c o n f i g ∗ my conf , u 8 t ∗ b u f 2 f i l l , u 8 t →


,→ l e n b u f )
{ const struct spi buf rx buf = {
. buf = b u f 2 f i l l ,
. len = len buf
};
const struct s p i b u f s e t s p i r x = {
. b u f f e r s = &r x b u f ,
. count = 1
};

i f ( s p i t r a n s c e i v e ( my dev , my conf , NULL, &s p i r x ) ) { r e t u r n 1 0 ; }


e l s e { return 0;}
}

v o i d main ( v o i d )
{ struct device ∗ spi ;
s p i = d e v i c e g e t b i n d i n g ( DT SPI 4 NAME ) ;
i f ( ! s p i ) { p r i n t k ( " Could not find SPI driver \ n " ) ; r e t u r n ; }

static struct spi config in read conf = {


. f r e q u e n c y = 256000U,
. o p e r a t i o n = (SPI OP MODE MASTER
// | SPI MODE CPOL
| SPI MODE CPHA
| SPI WORD SET ( 8 ) | SPI LINES SINGLE ) ,
. c s = NULL,
};

s t r u c t s p i c o n f i g ∗ r e a d c o n f = &i n r e a d c o n f ;

static struct spi config in write conf = {


. f r e q u e n c y = 256000U,
. o p e r a t i o n = SPI WORD SET ( 8 ) ,
};

7
s t r u c t s p i c o n f i g ∗ w r i t e c o n f = &i n w r i t e c o n f ;

u 8 t d j c b t x =0x55 ;
u8 t djcb rx [ 4 ] ;

while (1)
{ i f ( m y s p i r e a d ( s p i , r e a d c o n f , d j c b r x , s i z e o f ( d j c b r x ) ) ) { p r i n t k ( " Read error \ n " ) ; }
e l s e { show array ( djcb rx , s i z e o f ( djcb rx ) ) ; }
}
}
Un exemple de résultat de mesure lorsque nous faisons varier le potentiel d’une alimentation entre
les broches CH1- et CH1+ entre 0 et environ 2 V est fournie dans la Fig. 2.

4000 ’t’

3500

3000
ADC value (bit)

2500

2000

1500

1000

500

0
0 100000 200000 300000 400000 500000 600000
sample number (a.u.)

Figure 2 – Haut : résultat d’une mesure du LTC1407A connecté au port “Arduino” (SPI4) du
STM32MP157. Bas : chronogrammes des transactions sur bus SPI (en vert MISO, en jaune CLK, en
orange NSS connecté à CONV du convertisseur.

Nous constatons que l’envoi des mesures par la console virtuelle (interface trace0) induit de nom-
breuses pertes de données si le débit est aussi élevé que la mesure du convertisseur (quelque dizaines
de kcaractères/seconde). Il faut donc envisager une transaction par RPMSG en double-tampon protétée
par sémaphore pour garantir l’intégrité des messages échangés.

8
6 Lecture/écriture des registres du S2LP par SPI depuis le M4
On prendra soin de déplacer les résistances de CLK et CS# pour router ces signaux vers les broches
du connecteur compatible Arduino du STM32MP157C-DK2 et non vers leur position par défaut qui ne
correspond pas au routage de la carte mère 12 .
#i n c l u d e <e r r n o . h>
#i n c l u d e <z e p h y r . h>
#i n c l u d e <s y s / p r i n t k . h>
#i n c l u d e <d e v i c e . h>
#i n c l u d e <d r i v e r s / s p i . h>
#i n c l u d e <d r i v e r s / g p i o . h>

#d e f i n e N 12

v o i d s h o w a r r a y ( u 8 t ∗ my array , u 8 t a r r a y l e n )
{
u8 t inc ;
f o r ( i n c =0; i n c <a r r a y l e n ; i n c ++) p r i n t k ( " %02 x " , my array [ i n c ] ) ;
pr int k ( "\n" ) ;
}

v o i d main ( v o i d )
{ struct device ∗ spi ;
int k ;
p r i n t k ( " dans le main ( N =% d ) \ n " ,N) ;
s p i = d e v i c e g e t b i n d i n g ( DT SPI 4 NAME ) ;
i f ( ! s p i ) { p r i n t k ( " Could not find SPI driver \ n " ) ; r e t u r n ; }

static struct spi config spi conf = {


. f r e q u e n c y = 256000U,
. o p e r a t i o n = (SPI OP MODE MASTER
// | SPI MODE CPOL // | SPI MODE CPHA
| SPI WORD SET ( 8 ) | SPI LINES SINGLE ) ,
. cs = NULL,
};

u 8 t b u f 2 w r i t e 1 [ 4 ] = { 0 x00 , 0 x00 , 0 x42 , 0 x42 } ; // W#=0


u 8 t b u f 2 w r i t e 2 [ 2 ] = { 0 x01 , 0 x00 } ; // R=1
u 8 t b u f 2 r e a d [N ] ;

const struct spi buf t x b u f 1= { . buf = buf2write1 , . len = 4 };


const struct spi buf set s p i t x 1= { . b u f f e r s = &t x b u f 1 , . count = 1 };
const struct spi buf t x b u f 2= { . buf = buf2write2 , . len = 2 };
const struct spi buf set s p i t x 2= { . b u f f e r s = &t x b u f 2 , . count = 1 };

const struct spi buf r x b u f = { . buf = buf2read , . l e n = N } ;


c o n s t s t r u c t s p i b u f s e t s p i r x = { . b u f f e r s = &r x b u f , . count = 1 };

f o r ( k =0;k <12; k++)


{ p r i n t k ( " %02 d : " , k ) ;
// s p i t r a n s c e i v e ( s p i , &s p i c o n f , &s p i t x 1 , NULL) ;
// a v e c 52 00 01 00 42 00 42 00 a2 00 a2 00
// s a n s 52 00 01 00 0a 00 a2 00 a2 00 a2 00
buf2write2 [1]= k ;
s p i t r a n s c e i v e ( s p i , &s p i c o n f , &s p i t x 2 , &s p i r x ) ;
s h o w a r r a y ( b u f 2 r e a d , N) ;
k s l e e p ( 1 0 ) ; // ms
}
w h i l e ( 1 ) {}
}
dont le résultat est de la forme
12. page 7 de UM2405, “Getting started with the X-NUCLEO-S2868A1 Sub-1 GHz 868 MHz RF expansion board based
on S2-LP radio for STM32 Nucleo” qui indique de retirer R13 pour la placer en R9 et retirer R11 pour la placer en R6.

9
cd /sys/kernel/debug/remoteproc/remoteproc0/
echo stop > /sys/class/remoteproc/remoteproc0/state
echo start > /sys/class/remoteproc/remoteproc0/state
cat trace0
*** Booting Zephyr OS build zephyr-v2.1.0-581-g4e135d76a3c2 ***
dans le main (N=12)
00: 52 00 01 00 0a 00 a2 00 a2 00 a2 00
01: 52 00 01 00 a2 00 a2 00 a2 00 00 00
02: 52 00 01 00 a2 00 a2 00 00 00 42 00
03: 52 00 01 00 a2 00 00 00 42 00 16 00
04: 52 00 01 00 00 00 42 00 16 00 27 00
05: 52 00 01 00 42 00 16 00 27 00 62 00
06: 52 00 01 00 16 00 27 00 62 00 2a 00
07: 52 00 01 00 27 00 62 00 2a 00 b8 00
08: 52 00 01 00 62 00 2a 00 b8 00 00 00
09: 52 00 01 00 2a 00 b8 00 00 00 3f 00
10: 52 00 01 00 b8 00 00 00 3f 00 00 00
11: 52 00 01 00 00 00 3f 00 00 00 83 00

qui se lit 0a a2 a2 a2 00 42 16 27 62 en accord avec


la documentation technique. −→
Question : POURQUOI LES 0x00 ENTRE LES
BONNES VALEURS et 0x52 0x00 0x01 0x00 EN
DEBUT ?

Une fois la configuration validée, nous pouvons émettre en envoyant deux commandes [1, p.42] (bit
de poids le plus fort à 1 pour indiquer que l’octet suivant de la transaction SPI est une commande) :
0x62 passe la transceiver en mode READY et 0x60 le passe en mode émission.
u 8 t cmd1write1 [ 2 ] = { 0 x80 , 0 x62 } ; //CMD=1,W#=0
u 8 t cmd1write2 [ 2 ] = { 0 x80 , 0 x60 } ; //CMD=1,W#=0

const struct spi buf tx cmd1= \


{ . b u f = cmd1write1 , . l e n = 2 };
c o n s t s t r u c t s p i b u f s e t s p i c m d 1=\
{ . b u f f e r s = &tx cmd1 , . c o u n t = 1 };
const struct spi buf tx cmd2= \
{ . b u f = cmd1write2 , . l e n = 2 };
c o n s t s t r u c t s p i b u f s e t s p i c m d 2=\
{ . b u f f e r s = &tx cmd2 , . c o u n t = 1 };

s p i t r a n s c e i v e ( s p i ,& s p i c o n f ,& s p i c m d 1 , NULL) ;


k s l e e p ( 1 0 ) ; // ms
s p i t r a n s c e i v e ( s p i ,& s p i c o n f ,& s p i c m d 2 , NULL) ;
k s l e e p ( 1 0 ) ; // ms Bleu=sans émission, vert=TX
La fréquence de la porteuse émise en onde continue est en accord avec la configuration par défaut :
les 28 bits de configuration de la PLL [1, p.66] valent SY N T =0x2162762 avec BS=0 pour indiquer que
la bande de fréquence haute est utilisée et donc B=4, D=1 (puisque REFDIV=0). Ainsi la fréquence de
porteuse est
fXO × SY N T
f= B 20
2 ×D×2

qui vaut, avec fXO = 50 MHz de la carte d’évaluation de ST, 834,62 MHz. À l’écart de l’oscillateur local
du récepteur de DVB-T, l’observation semble cohérente avec les attentes.

10
La programmation des registres de confi-
guration de la PLL se traduit bien par un
changement de la fréquence porteuse : dans
cet exemple, nous coupons l’alimentation du
S2-LP pour reconfigurer la fréquence (re-
trait du jumper JP1) – il est probable que
quitter le mode READY avant la program-
mation de la fréquence permettrait d’éviter
de couper l’alimentation. Faire varier de
0x6000 le mot de configuration se traduit
u 8 t b2wr1 [ 4 ] = { 0 x00 , 0 x07 , 0 x87 , 0 x62 } ; // W#=0
c o n s t s t r u c t s p i b u f t x b u f 1 ={. b u f=b2wr1 , . l e n =4};
par une variation de fréquence de 586 kHz,
c o n s t s t r u c t s p i b u f s e t s p i t x 1= en accord avec les observations à l’analyseur
{ . b u f f e r s = &t x b u f 1 , . count = 1 } ; de spectre du récepteur DVB-T (fréquence
s p i t r a n s c e i v e ( s p i , &s p i c o n f , &s p i t x 1 , NULL) ; d’échantillonnage : 1,5 MHz).

7 Ce qui reste à faire


1. voir si un ring buffer peut éviter les blocs disjoints des mesures par IIO sur les ADCs lus depuis
GNU/Linux
2. envoi d’un compteur depuis le M4 vers le A7 pour une mesure de débit
voir https://community.st.com/s/question/0D50X0000AnuE1XSQU/what-is-the-maximum-transmission-int
rpmsg est conçu pour échanger des messages et non des données, il faut passer par un shared buffer
pour échanger des informations.
Voir https://community.st.com/s/question/0D50X0000BaKRu7SQG/how-to-increase-the-rpmsg-buffer-si
pour la définition des buffers
3. lecture ADC et/ou SPI depuis le M4 et transfert au A7 par le mécanisme vu ci-dessus
4. A7 : source IIO avec un compteur qui peut être lu par gr-iio
5. compteur sur M4 transmis par rpmsg au A7 qui alimente la source IIO lue par gr-iio
6. source GNU Radio ou être compatible gr-iio en prenant les données lues sur ADC ou SPI du
M4 et en les injectant dans GNU Radio sur le processeur A7.

Références
[1] S2LP datasheet – Ultra-low power, high performance, sub-1 GHz transceiver (DS11896 - Rev 6 - June
2019)

11

Vous aimerez peut-être aussi