Elektor Electronics 2018 03 04
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
On trouve ici deux particularités importantes. D’abord la section<br />
(vide ici) .bss, dont l’absence fait que, même si le processeur<br />
ULP trouve le fichier, il refuse sans commentaire de l’exécuter.<br />
Ensuite, nous avons une séquence qui crée une variable globale<br />
partagée entre l’ULP et Xtensa. Nous pouvons alors poursuivre<br />
avec le code effectif :<br />
entry:<br />
/* Code goes into .text section */<br />
.text<br />
.global entry<br />
move r3, acti_count<br />
ld r0, r3, 0<br />
add r0, r0, 1<br />
st r0, r3, 0<br />
Les processeurs ULP sont des machines à 16 bits. À la<br />
lecture, une partie du registre n’est pas prise en compte. Pour<br />
commencer, nous décalons l’adresse de acti_count dans le<br />
demi-registre R3, que nous utilisons pour lire la valeur. Celle-ci<br />
est incrémentée avec add et réécrite dans acti-count.<br />
Pour finir, nous réveillons le côté Xtensa. Ici, nous avons une<br />
petite diffculté : les commandes de réveil ne sont acceptées<br />
qu’une fois que le noyau a été arrêté avec succès. C’est pourquoi<br />
notre code n’exécute wake que lorsque le SOC (système sur<br />
puce) a confirmé sa mise en sommeil dans le registre concerné :<br />
de l’ULP du côté C.<br />
Ici, la situation exige une réflexion approfondie. La partie<br />
Xtensa du microcontrôleur contient une quantité de registres<br />
dont la préservation du contenu exige une dépense d’énergie<br />
considérable. Pour minimiser cette dépense, Espressif coupe<br />
l’alimentation des registres, ce qui entraîne malheureusement la<br />
perte de leur contenu. La première action de notre programme<br />
est donc de déterminer la cause du réveil :<br />
void app_main() {<br />
//Init check<br />
esp_sleep_wakeup_cause_t cause =<br />
esp_sleep_get_wakeup_cause();<br />
if (cause != ESP_SLEEP_WAKEUP_ULP) {<br />
printf("Clean boot\n");<br />
init_ulp_program();<br />
} else {<br />
printf("Start caused by ULP\n");<br />
printf("Counter %u \n", ulp_acti_count &<br />
UINT16_MAX);<br />
}<br />
Il est important ici de masquer le contenu de ulp_acti_<br />
count qui, comme tous les registres de Xtensa, a une largeur<br />
de 32 bits. Comme l’ULP ne peut écrire que dans les seize<br />
premiers, il faut masquer le reste qui est « indéfini ».<br />
exit:<br />
halt<br />
/* Check if the SoC has said INRI already */<br />
READ_RTC_REG(RTC_CNTL_DIAG0_REG, 19, 1)<br />
and r0, r0, 1<br />
jump exit, eq<br />
wake<br />
WRITE_RTC_FIELD(RTC_CNTL_STATE0_REG, RTC_CNTL_<br />
ULP_CP_SLP_TIMER_EN, 0)<br />
halt<br />
halt<br />
Côté C<br />
Ensuite, nous retournons au programme hello_world_main.c<br />
dans lequel nous incluons quelques fichiers d’en-tête :<br />
#include "esp_sleep.h"<br />
#include "nvs.h"<br />
#include "nvs_flash.h"<br />
#include "soc/rtc_cntl_reg.h"<br />
#include "soc/rtc_io_reg.h"<br />
#include "soc/sens_reg.h"<br />
#include "soc/soc.h"<br />
#include "driver/gpio.h"<br />
#include "driver/rtc_io.h"<br />
#include "esp32/ulp.h"<br />
#include "ulp_main.h"<br />
ulp_main est créé par Makefile : le fichier prépare les contenus<br />
nécessaires au traitement ou au dialogue avec le programme<br />
Trop compliqué !<br />
Si l’on rechigne à travailler au niveau de make (ou si l’on a<br />
affaire à de très vieux exemples de code), on peut utiliser les<br />
macros (mais elles ont depuis été abandonnées par Espressif).<br />
Dans ce cas, un programme ULP est produit à partir d’un<br />
tableau d’instructions :<br />
const ulp_insn_t program[] = {<br />
I_MOVI(R3, 16),<br />
I_LD(R0, R3, 0),<br />
I_LD(R1, R3, 1),<br />
I_ADDR(R2, R0, R1),<br />
I_ST(R2, R3, 2),<br />
I_HALT()<br />
};<br />
La suite est constituée d’une séquence de code C plus ou<br />
moins standard qui calcule la taille du programme en instructions<br />
et fournit le résultat pour exécution. Il faut prendre<br />
garde à toujours faire le calcul de size comme indiqué cidessous<br />
– certaines macros sont interprétées par deux instructions<br />
séparées :<br />
size_t load_addr = 0;<br />
size_t size = sizeof(program)/sizeof(ulp_insn_t);<br />
ulp_process_macros_and_load(load_addr, program,<br />
&size);<br />
ulp_run(load_addr);<br />
www.elektormagazine.fr mars/avril <strong>2018</strong> 35