Définition et manipulation de registres
publication: 2 avril 2022 / mis à jour 19 juillet 2022
Appel à collaboration
Vous développez des montages, simples ou complexes avec ESP32 et ESP32forth.
Partagez-les ici sur ce site.
ESP32forth ne pourra se développer qu'avec la collaboration active de toutes les bonnes volontés.
Vos montages peuvent aider d'autres développeurs.
Les montages des autres développeurs peuvent vous aider.
Pour proposer un article ou un montage, cliquez ici
Les registres ESP32
Extrait de la page 631 de ESP32 Technical Reference Manual
Dans ce document, on retrouve une très grande quantité de registres. Ce sont ces registres qui permettent de contrôler tous les périphériques et ports GPIO de la carte ESP32.
La manipulation de ces registres est effectuée par la couche applicative proposée par ESP32forth. Il n'est donc pas nécessaire d'y accéder directement.
Mais comme on l'a vu dans cet article Acces direct aux registres GPIO, il peut être intéressant de gérer certains registres directement:
- pour accéder à des fonctionnalités non proposées par les mots FORTH
- pour exécuter plus rapidement du code FORTH
Définition de registres
Définir un registre est très simple:
$3FF48898 constant SENS_SAR_DAC_CTRL1_REG \ DAC control
Le premier inconvénient, en définissant un registre comme constante, c'est qu'à la lecture du code source, on ne pourra pas distinguer le registre des autres constantes. On va donc définir un mot de création de registres comme ceci:
\ define a register, similar as constant : defREG: create ( addr1 --) , does> ( -- regAddr ) @ ; $3FF48898 defREG: SENS_SAR_DAC_CTRL1_REG \ DAC control
De cette manière, en relisant notre code, nous savons que le mot créé est un
registre. L'autre intérêt est qu'on peut modifier defREG:
pour en
changer le comportement: rajout de tests de contrôles, initialisation de
paramètres, etc...
Accès au contenu des registres
Valeurs des bits dans le registre SENS_SAR_DAC_CTRL1_REG:
Ce registre contient des bits ou blocs de bits ayant des fonctions définies.
Nous allons d'abord créer un mot permettant de visualiser le contenu d'un registre:
\ display reg content : .reg ( reg -- ) base @ >r binary @ <# 4 for aft 8 for aft # then next bl hold then next #> cr space ." 33222222 22221111 11111100 00000000" cr space ." 10987654 32109876 54321098 76543210" cr type r> base ! ;
Voyons ce que donne le contenu de notre registre SENS_SAR_DAC_CTRL1_REG:
SENS_SAR_DAC_CTRL1_REG .reg \ display: 33222222 22221111 11111100 00000000 10987654 32109876 54321098 76543210 00000000 00000000 00000000 00000000 ok
Les deux premières lignes permettent de lire verticalement le rang d'un bit dans ce registre, ici, en rouge, 25, dont le contenu est 0.
Pour lire ce bit, on procède comme suit:
SENS_SAR_DAC_CTRL1_REG @ 1 25 lshift and
Pour modifier ce bit et le mettre à 1:
1 25 lshift SENS_SAR_DAC_CTRL1_REG @ or SENS_SAR_DAC_CTRL1_REG !
Vérifions avec .reg
:
SENS_SAR_DAC_CTRL1_REG .reg \ display: 33222222 22221111 11111100 00000000 10987654 32109876 54321098 76543210 00000010 00000000 00000000 00000000 ok
Si c'est pour une fois, ça dépanne. Voyons comment faire de manière plus efficace...
Manipulation des bits des registres
Reprenons la modification du bit 25 de notre registre SENS_SAR_DAC_CTRL1_REG, voici comment mettre à 1 le bit b25:
SENS_SAR_DAC_CTRL1_REG .reg \ display: \ 33222222 22221111 11111100 00000000 \ 10987654 32109876 54321098 76543210 \ 00000000 10000000 00000000 00000000 ok registers 1 25 $02000000 SENS_SAR_DAC_CTRL1_REG m! SENS_SAR_DAC_CTRL1_REG .reg \ display: \ 33222222 22221111 11111100 00000000 \ 10987654 32109876 54321098 76543210 \ 00000010 00000000 00000000 00000000 ok
On utilise le mot m! ( val shift mask addr -- )
qui accepte quatre paramètres:
- val qui est la valeur à modifier, ici 1
- shift qui correspond au décalage à appliquer à cette valeur, ici 25
- mask qui correspond au masque logique de la partie de registre à modifier, ici $02000000
- addr qui est l'adresse du registre, ici SENS_SAR_DAC_CTRL1_REG
Définition de masques
Un masque sert à indiquer quels sont les bits modifiables. Dans le précédent exemple, nous avons modifié le bit b25. Dans la documentation Espressif, le bit b25 est marqué avec le label SENS_DAC_CLK_INV. La solution la plus simple consisterait à créer une constante comme ceci:
1 25 lshift constant SENS_DAC_CLK_INV
Mais ça ne règle pas le décalage de valeur qui doit être le même que la valeur du masque binaire.
Voyons une manière plus élégante de définir les masques:
: defMASK: create ( mask0 position -- ) dup , lshift , does> ( -- position mask1 ) dup @ swap cell + @ ; 1 25 defMASK: mSENS_DAC_CLK_INV
Au passage, notez que le nom du masque est préfixé avec la lettre 'm' (pour mask). Ce n'est nullement obligatoire. Mais quand vous aurez compilé de nombreux registres et masques, le préfixe 'm' permettra de vous y retrouver entre registres et masques:
--> words mSENS_SW_FSTEP mSENS_SW_TONE_EN mSENS_DAC_DIG_FORCE mSENS_DAC_CLK_FORCE_LOW mSENS_DAC_CLK_FORCE_HIGH mSENS_DAC_CLK_INV defMask: defBit: SENS_SAR_DAC_CTRL2_REG SENS_SAR_DAC_CTRL1_REG GPIO_ENABLE_W1TC_REG GPIO_ENABLE_W1TS_REG GPIO_ENABLE_REG GPIO_OUT_W1TC_REG GPIO_OUT_W1TS_REG GPIO_OUT_REG DR_REG_GPIO_BASE PIN_DAC2 PIN_DAC1 CONFIG_IDF_TARGET_ESP32S3 CONFIG_IDF_TARGET_ESP32S2 .reg AdcREG: mtst mset mclr --DAdirect SENS_DAC_CLK_INV defMASK: input$ c+$! mid$ left$ right$ 0$! $! maxlen$ string $= FORTH camera-server camera telnetd bterm ......
Le mot défini avec defMASK:
dépose sur la pile le décalage du masque,
ici 25 pour mSENS_DAC_CLK_INV
et la valeur du masque binaire à appliquer.
Reprenons la modification du bit b25 avec cette définition de masque:
1 mSENS_DAC_CLK_INV SENS_SAR_DAC_CTRL1_REG m! SENS_SAR_DAC_CTRL1_REG .reg \ display: \ 33222222 22221111 11111100 00000000 \ 10987654 32109876 54321098 76543210 \ 00000010 00000000 00000000 00000000 0 mSENS_DAC_CLK_INV SENS_SAR_DAC_CTRL1_REG m! SENS_SAR_DAC_CTRL1_REG .reg \ display: \ 33222222 22221111 11111100 00000000 \ 10987654 32109876 54321098 76543210 \ 00000000 00000000 00000000 00000000
Passer du langage C au langage FORTH
Avec ESP32Forth, il y a deux solutions pour rajouter des primitives au dictionnaire FORTH:
- réécrire le code source de ESP32Forth en rajoutant les primitives souhaitées;
- réécrire ces mots du langage C en FORTH
La première solution, en langage C est écrite ici:
#ifndef ENABLE_DAC_SUPPORT # define OPTIONAL_DAC_SUPPORT # else # include <driver/dac.h> # include <driver/dac_common.h> # include <soc/rtc_io_reg.h> # include <soc/rtc_cntl_reg.h> # include <soc/sens_reg.h> # include <soc/rtc.h> # define OPTIONAL_DAC_SUPPORT \ Y(dac_output_enable, n0 = dac_output_enable( (dac_channel_t) n0 ) ) \ Y(dac_output_disable, n0 = dac_output_disable( (dac_channel_t) n0 ) ) \ Y(dac_output_voltage, n0 = dac_output_voltage((dac_channel_t ) n1, (gpio_num_t ) n0); NIP ) \ Y(dac_cw_generator_enable, PUSH dac_cw_generator_enable () ) \ Y(dac_cw_generator_disable, PUSH dac_cw_generator_disable () ) \ Y(dac_i2s_enable, PUSH dac_i2s_enable() ) \ Y(dac_i2s_disable, PUSH dac_i2s_disable() ) \ Y(rtc_freq_div_set, REG_SET_FIELD(RTC_CNTL_CLK_CONF_REG, RTC_CNTL_CK8M_DIV_SEL, n0 ); DROP ) \ Y(dac_freq_step_set, SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL1_REG, SENS_SW_FSTEP, n0, SENS_SW_FSTEP_S); DROP ) \ Y(dac1_scale_set, SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_SCALE1, 0, SENS_DAC_SCALE1_S); ) \ Y(dac2_scale_set, SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_SCALE2, 0, SENS_DAC_SCALE2_S); ) \ Y(dac1_offset_set, SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_DC1, n0, SENS_DAC_DC1_S); DROP ) \ Y(dac2_offset_set, SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_DC2, n0, SENS_DAC_DC2_S); DROP ) \ Y(dac1_invert_set, SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV1, n0, SENS_DAC_INV1_S); DROP ) \ Y(dac2_invert_set, SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV2, n0, SENS_DAC_INV2_S); DROP ) \ Y(dac1_cosine_enable, SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN1_M); ) \ Y(dac2_cosine_enable, SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN2_M); ) \ Y(dacWrite, dacWrite(n1, n0); DROPn(2)) #endif
La seconde solution consiste à regarder le code source d'un fichier écrit en langage C et essayons de comprendre comment les registres sont manipulas dans ce langage.
Extrait du fichier dac-cosine.c:
/* * Enable cosine waveform generator on a DAC channel */ void dac_cosine_enable(dac_channel_t channel) { // Enable tone generator common to both channels SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL1_REG, SENS_SW_TONE_EN); switch(channel) { case DAC_CHANNEL_1: // Enable / connect tone tone generator on / to this channel SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN1_M); // Invert MSB, otherwise part of waveform will have inverted SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV1, 2, SENS_DAC_INV1_S); break; case DAC_CHANNEL_2: SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN2_M); SET_PERI_REG_BITS(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_INV2, 2, SENS_DAC_INV2_S); break; default : printf("Channel %d\n", channel); } }
Une des fonctions C qui revient souvent est SET_PERI_REG_MASK. Cette fonction met à 1 les bits désignés par un masque dans un registre. Exemple:
SET_PERI_REG_MASK(SENS_SAR_DAC_CTRL2_REG, SENS_DAC_CW_EN1_M);
La fonction C qui met à 0 les bits désignés par un masque dans un registre est CLEAR_PERI_REG_MASK.
On va s'intéresser à la manière dont on va réécrire dac_cosine_enable(dac_channel_t channel)
en langage FORTH.
SENS_SAR_DAC_CTRL2_REG
est mentionné. On va donc définir ce registre:
$3FF4889c defREG: SENS_SAR_DAC_CTRL2_REG \ DAC output control
Dans ce registre SENS_SAR_DAC_CTRL2_REG
, les deux bits qui nous intéressent sont
b24 et b25. Définissons les masques correspondants:
1 24 defMASK: mSENS_DAC_CW_EN1 \ selects CW generator as source for PDAC1 1 25 defMASK: mSENS_DAC_CW_EN2 \ selects CW generator as source for PDAC2
Re-writing in progress / Ré-écriture en cours
Conclusion
La programmation 'bare-metal' agissant directement sur les registres de ESP32 ne nécessite pas de définir en FORTH tous les registres et masques de registres comme le fait le langage C. Limitez-vous aux registres et masques essentiels pour votre application.
Il est conseillé d'utiliser les noms des registres et masques de registres tels que figurant dans la documentation Espressif, ou à défaut les noms de registres utilisés dans les codes sources en langage C.
La gestion de certains périphériques est très complexe. La documentation Espresif est avare d'exemples sur l'utilisation directe des registres.
Legal: site web personnel sans commerce / personal site without seling