Cravate compliquée
Arrière-plan ...
Dans le cadre de mon travail sur l'ingénierie inverse des étiquettes de prix électroniques, j'ai rencontré un problème intéressant. Une entreprise spécifique (Samsung Electro Mechanics / SoluM) est passée de l'utilisation de puces tierces, dont j'ai pu identifier l'origine (Marvell 88MZ100) à une nouvelle puce, qu'elle a commencé à utiliser avec ses étiquettes de prix de nouvelle génération.
Il semble que ce soit leur propre puce, développée par la société dans ce but précis. Assumer la rétro-ingénierie d'une telle chose est un problème mort. Un ami m'a donné des étiquettes de prix avec de telles puces - à bricoler. Il s'est avéré qu'ils étaient de deux types: l'un avec un affichage segmenté sur e-ink, et l'autre avec un affichage graphique classique sur e-ink. La puce principale des deux modèles est la même, donc la première chose que j'ai faite a été avec un dispositif d'affichage segmenté, car il est plus simple et il est plus facile de gérer un système inconnu qui l'utilise. Il n'était pas tout à fait clair par où commencer, mais, bien sûr, ce sont les tâches qui sont toujours les plus intéressantes!
Étude
Il est insensé d'essayer de résoudre un puzzle de mots croisés sans y lire les questions. Il est tout aussi insensé de rétroconcevoir un appareil sans collecter au préalable toutes les informations déjà disponibles à son sujet. Alors, que savons-nous au départ? Le protocole de transfert de données sans fil est probablement le même que d'habitude, car aucune entreprise ne souhaite migrer vers un nouveau protocole ou prendre en charge deux protocoles pour ses clients à la fois, en effectuant lentement la migration. L'ancien protocole était de type ZigBee 2,4 GHz, donc le nouveau est probablement le même. Voici une photo de la planche des deux côtés.
Alors que voyons-nous? Tout d'abord, un bon exemple d'optimisation des coûts. Ils ont laminé l'écran e-ink directement sur le PCB! Qui a besoin d'un panneau arrière en verre conducteur lorsqu'il y a un PCB? Le panneau avant est en plastique conducteur. Mais ce n'est pas important.
Deux antennes sont visibles, toutes les deux, à en juger par leur taille - à 2,4 GHz. Comme prévu, puisque les appareils de la génération précédente disposaient également de deux antennes 2,4 GHz. Nous voyons deux puces. Grand et petit. Le grand (désigné "SEM9010") a apparemment beaucoup de contacts allant à l'écran et aucun aux antennes. Il s'agit évidemment d'un contrôleur d'affichage.
Le petit (désigné "SEM9110") semble être le cerveau responsable de toutes les opérations. Il est connecté aux antennes, au cristal de synchronisation et aux points clés qui sont évidents ici pour la programmation d'usine.
Il y a 12 pads ici: l'un est connecté à la borne positive de la batterie, l'un à la terre, le but des 10 autres est un mystère. En cherchant le nom de la puce en ligne, je ne trouve rien d'utile - certainement leur propre développement. Mais qui conçoit sa propre puce pour une application aussi simple? Peut-être juste un changement de marque? Mobilisés, nous travaillons!
Curieusement, la recherche d'images Google a aidé ici. Il arrive que cet outil soit utile pour la rétro-ingénierie. Dans ce cas, il nous amène à cette pépite. (copie archivée ici pour la postérité). C'est une question de StackExchange - se demandant comment fonctionnent ces étiquettes électroniques. La question est intéressante car sur la photo postée ici, le circuit imprimé est presque identique au nôtre. Les puces sont également exactement les mêmes, mais les étiquettes dessus sont différentes! La carte a probablement été fabriquée avant que SoluM ne commence à renommer ces puces.
La puce que j'ai supposée être le contrôleur d'affichage est étiquetée
SSD1623L2
. En effet, il s'agit d'un contrôleur d'affichage segmenté e-ink qui prend en charge jusqu'à 96 segments. En cherchant en ligne, je trouve la fiche technique de la pré-version 0.1 (copie archivée ici pour la postérité). C'est bon! S'ils savaient comment s'y prendre, ils pourraient ramasser un code qu'il comprend, et dès que nous voyons ce code, c'est tout!
Il s'avère que le microcontrôleur principal est
ZBS242
. D'accord. Je ne connais pas ce microcontrôleur. Cherchons un peu plus sur Internet - et les recherches nous mènent au lien (copie d'archive ici pour la postérité), qui mentionne également la même réponse de StackExchange. La page est coréenne, mais elle montre que cette puce a un cœur 8051, ainsi qu'un équipement périphérique assez prévisible: UART, SPI, I2C, ADC, DAC, comparateur, capteur de température, PWM 5 canaux, contrôleur triac 3 canaux , Émetteur IR, fonction de balayage des touches, fonction RF-Wake, espacement des antennes, radio compatible ZigBiee et MAC. L'image montre qu'il existe également un oscillateur RC interne de 32 kHz, qui, comme indiqué, peut consommer aussi peu que 1 uA en mode veille. Je pense que c'est cette société qui a fabriqué notre puce pour Samsung. Intéressant ...
Regardons les photos et constatons que le cristal SEM9110 qui nous a intrigués a également été tourné à bout portant (copie d'archive ici pour la postérité). Il est indiqué qu'il s'agit de ZBS243. Je suppose que cela signifie qu'il y a toute une famille de puces ici: le ZBS24x. Vraiment intéressant.
Nous avons un fil!
Après avoir ouvert une autre étiquette de segment, nous continuons à nous réjouir de cette nouvelle: la tête de programmation est signée en lettres dorées claires et lisibles! La tête semble avoir un SPI, UART, une broche de réinitialisation, une alimentation, une masse et une broche appelée «test», probablement utilisée pour entrer en mode de test d'usine. Tout est plus curieux et curieux.
Il est logique que le plus ancien représentant de la famille hypothétique ZBS24x soit désigné "ZBS240". Peut-être qu'une recherche pour une telle requête nous donnera quelque chose d'intéressant? En recherchant "ZBS240" et en filtrant le laitier, nous trouvons une autre page intéressante en coréen (copie archivée ici pour la postérité). Il semble que cette entreprise fabrique des programmeurs de groupe personnalisés à la demande. Après avoir regardé autour de leur site Web, nous trouvons un manuel (copie d'archive ici pour la postérité) sur leur appareil de programmation, et nous pouvons même télécharger un utilitaire pour qu'un PC fonctionne avec un tel appareil. Cet utilitaire dispose même d'un outil pour mettre à jour le firmware de l'appareil. J'ai regardé pour voir s'il était possible de deviner à partir de ces informations comment programmer l'appareil, mais le firmware s'est avéré être crypté. Apparemment, l'utilitaire côté PC envoie simplement des données via le port série USB, il n'y a donc pas non plus d'informations utiles ici. Triste ...
Après avoir cherché un peu plus, nous trouvons une page encore plus intéressante (copie archivée ici pour la postérité). Qu'est-ce que c'est? Est-ce en solde?!? Certainement plus, non? Je viens d'écrire à cette société pour du savon, juste au cas où. Silence ... En signe de désespoir, j'ai demandé à un ami de Hong Kong s'il connaissait quelqu'un en Corée qui pourrait contacter ces gars, car leur site Web montre qu'ils n'acceptent qu'un virement d'une banque coréenne comme moyen de paiement. J'étais juste étonné quand il a frappé en arrière et a dit, en effet, il pourrait me procurer cet appareil par un intermédiaire trouvé en Corée! Quelques jours plus tard, l'appareil a été livré par DHL!
Vous pouvez l'atteindre!
Comment le contacter
Travaux! Je peux lire la puce et y écrire. Il m'a fallu un certain temps pour rechercher l'outil de programmation. Apparemment, la puce a 64 Ko de mémoire flash et un "bloc d'informations" de 1 Ko, qui, je crois, est utilisé pour stocker des valeurs d'étalonnage, des adresses MAC, etc. J'ai pu intercepter certaines traces, armé du merveilleux analyseur logique Saleae Logic , en regardant le programmeur faire son travail. Vous pouvez télécharger mes résultats ici . Dans cette archive, vous trouverez des traces de lecture, d'effacement et d'écriture dans les espaces INFOBLOCK et CODE. En fait, le protocole est TRÈS simple! La fréquence d'horloge peut être comprise entre 100 kHz et 8 MHz.
Protocole ISP: coupé jusqu'à l'os
Tout commence par régler les lignes à l'état souhaité: SCLK bas, MOSI haut, RESET haut, SS haut. Cette condition est maintenue pendant 20 ms. Ensuite, RESET est abaissé de 32 ms. Ensuite, au moins 4 horloges de processeur sont envoyées à la ligne SCK à 500 kHz. Ensuite, il y a un autre délai de 10 ms jusqu'à ce que RESET soit poussé vers le haut. Vous pouvez maintenant définir un délai de 100 ms avant de démarrer la communication. Après cela, n'importe quel nombre de transactions peut être effectué. Quelques règles de base: il doit y avoir au moins 5us entre SS descendant et envoi d'un octet, au moins 2us entre la fin de l'octet et SS montant, et la période la plus courte que SS peut passer est de 2,5us. Par conséquent, chaque octet est envoyé dans l'état: SS est en panne, un octet est envoyé en mode SPI 0, SS est en haut. Oui bien sûr,SS retourne pour chaque octet.
Toutes les transactions ont une longueur de trois à quatre octets. Le premier octet indique le type de transaction, le bit le plus bas spécifie le sens de la transaction: zéro signifie écrire sur le périphérique, un signifie lire depuis le périphérique. Les commandes
0x02
/
0x03
sont utilisées pour lancer des sessions de communication. Le programmeur envoie une écriture de trois octets:
02 BA A5
puis lit, en envoyant d'abord la commande de lecture et «adresse» :, le
03 BA
maître envoie
FF
lors de la réception
A5
. Si cela fonctionne, la communication est établie.
Commandes
0x12
/
0x13
sont utilisés pour lire / écrire des registres à usage spécial (SFR) dans le processeur (j'ai trouvé cela plus difficile, mais dans ce cas, l'ordre n'est pas si important). Pour sélectionner INFOBLOCK, SFR
0xD8
doit être réglé sur
0x80
, pour sélectionner la zone principale du flash, il doit être réglé sur
0x00
. Pour écrire la valeur de vv dans le registre rr, des données SPI sont nécessaires
12 rr vv
. Pour s'assurer que la valeur a bien été lue, elle peut être relue en envoyant d'abord une commande de lecture et une "adresse":
13 rr
après quoi le maître envoie
FF
en réception
vv
.
Il est facile de lire la mémoire flash. Pour ce faire, appliquez
0x09
, une commande de quatre octets. Après l'octet de commande, l'adresse est envoyée, d'abord l'octet haut, puis l'octet bas. Le maître envoie alors
FF
, en attendant la réception de l'octet lu. Hé bien oui. Une commande distincte est requise pour lire chaque octet. L'écriture est également facile. Pour cela, la commande est utilisée
0x08
. Il s'agit d'une commande à quatre octets. Après l'octet de commande, l'adresse est envoyée, d'abord l'octet haut, puis l'octet bas, puis l'octet à écrire. Une commande distincte est également requise pour écrire chaque octet. Assurez-vous d'effacer avant l'enregistrement. Pour INFOBLOCK effacement, il ne nécessite qu'une seule séquence de 4 octets:
48 00 00 00
. L'effacement de la mémoire flash principale s'effectue à l'aide de la commande
88 00 00 00
.
Alors maintenant, vous en savez assez pour programmer trivialement votre ZBS24x!
Mets-toi au travail!
Apprêt pour 8051
Si vous connaissez déjà le 8051, vous pouvez ignorer cette section en toute sécurité .
Le 8051 est un ancien microcontrôleur conçu par Intel dans l'Antiquité . Il est très difficile de travailler avec, mais il est encore utilisé assez souvent car la licence est bon marché (en fait, gratuite). Quel est le problème? Le 8051 dispose de plusieurs espaces mémoire séparés.
CODE
Est la zone de mémoire réservée au code. Sa taille maximale est de 64 Ko (adresse 16 bits). Dans les conceptions les plus modernes, il s'agit de la mémoire flash. Le code peut lire des octets à partir d'ici à l'aide d'une instruction spéciale
movc
("MOVe from Code").
XRAM
Est la mémoire «externe». Autrement dit, externe au noyau. Vous pouvez y stocker diverses choses, mais c'est presque inutile pour autre chose. Comme ceci: les seules opérations qui peuvent être effectuées dans cette mémoire sont l'écriture et la lecture. Sa taille maximale est de 64 Ko (adresse 16 bits). Comment fonctionne la mémoire d'adresses d'une adresse 8 bits avec une adresse de 16 bits de large? Cela s'avère très lent. La commande
movx
("MOVe to / from eXternal") accède à ce type de mémoire, mais comment spécifier une adresse 16 bits? Pour cela, un registre spécial appelé
DPTR
("Data PoinTeR") est utilisé, ainsi que pour travailler avec une instruction
movc
.
DPTR
se compose d'un registre supérieur
DPH
et d'un registre inférieur
DPL
... Par conséquent, en écrivant la moitié de l'adresse dans chacun d'eux, vous pouvez adresser la mémoire externe et la mémoire de code. Comme vous pouvez le deviner, ce processus commence rapidement à glisser, car, par exemple, pour copier une section de la mémoire externe vers la mémoire externe, vous devrez mélanger à plusieurs reprises les valeurs entre
DPL
et
DPH
. Pour cette raison, certaines des versions les plus avancées du 8051 ont de nombreux registres
DPTR
, mais pas tous, et tous ne sont pas implémentés de la même manière.
Intel a ajouté un moyen plus rapide d'accéder à un sous-ensemble de mémoire externe. Dans ce cas, l'idée est d'utiliser des registres
R0
et
R1
comme registres de pointeur. Mais ils ont une taille de 8 bits, d'où viennent les 8 autres bits de l'adresse? Ils proviennent d'un registre
P2
(qui contrôle également le port 2 pour les broches GPIO). De toute évidence, cette pratique empêche d'utiliser le port 2 pour ... vous savez ... GPIO. Il existe des moyens de régler cette situation, mais je ne parle pas de cela maintenant. Ainsi, la quantité de mémoire dont nous disposons est limitée à 256 octets (sauf si vous changez dynamiquement le port 2, ce que vous ne souhaitez probablement pas faire). Habituellement, cette mémoire est appelée
PDATA
. Des accès mémoire similaires sont également effectués à l'aide d'une instruction
movx
. Suivant en ligne, nous avons
SFR
- divers registres de configuration avec lesquels les périphériques sont configurés. Cette zone mémoire n'est accessible que directement. Voici la situation: l'adresse doit être encodée directement dans l'instruction, il n'y aura pas d'accès via aucun registre de pointeur. Il y a 128 octets
SFR
. Le tableau suivant présente les listes
SFR
disponibles conformément à la norme 8051. Les cases grises contiennent les
SFR
bits accessibles individuellement à l'aide de commandes par bit. Ceci est utile lors de l'attribution de broches de port atomiques, ou lors de l'activation / désactivation des sources d'interruption, ou lors de la vérification de certains états.
La mémoire interne du 8051 est un peu délicate. Sur tous les 8051 modernes, il est de 256 octets. Les 128 derniers octets
0x80-0xff
sont disponibles uniquement indirectement par les registres
R0
et
R1
, mais, contrairement à la situation avec une mémoire externe, maintenant non seulement lire et écrire sont à notre disposition . Nous pouvons effectuer une augmentation de un (
inc
rement), une baisse de un (
dec
rement), addition (
add
) et la plupart des autres opérations attendues. En fait, TOUTE la RAM interne est accessible indirectement via ces registres de pointeurs. 128 octets les plus bas
0x00-0x7f
également disponible directement (l'adresse est directement encodée dans l'instruction elle-même, tout comme lorsque vous travaillez avec
SFR
. 16 octets de mémoire dans la plage sont
0x20-0x2f
également adressables en bits à l'aide d'instructions de traitement de bits. Il est pratique de stocker des variables pour des valeurs booléennes dans Cette partie. Les 32 octets les plus bas
0x00-0x1f
composent 4 registres de banques
R0
...
R7
Dans le registre d'état,
PSW
il y a des bits qui vous permettent de sélectionner la banque actuellement utilisée, mais en réalité, comme il y a généralement un manque dans la zone de mémoire interne, le code n'utilise généralement qu'une seule banque de mémoire.
Le 8051 est une machine principalement conçue pour fonctionner avec un seul opérande. Autrement dit: dans la plupart des opérations, la batterie est utilisée comme l'une des sources et, éventuellement, comme destination. Les registres peuvent également être utilisés pour de nombreuses opérations (mais pas toutes), et certaines opérations permettent un accès indirect à la RAM interne, comme décrit ci-dessus. La pile est un amont vide, adressable
SFR
, elle est appelée
sp
et se trouve uniquement dans la RAM interne, sa taille maximale est limitée à 256 octets, mais en réalité elle est beaucoup plus petite.
Toute image ROM 8051 commence par une table vectorielle qui contient des sauts vers le code initial que vous souhaitez exécuter ainsi que les gestionnaires d'interruption. En 8051, historiquement, le vecteur de réinitialisation est situé à
0x0000
et les gestionnaires d'interruption démarrent à l'adresse
0x0003
, puis tous les 8 octets. Étant donné que l'instruction
reti
n'est utilisée que pour renvoyer des interruptions, elle peut être utilisée pour détecter facilement si une fonction particulière est un gestionnaire d'interruptions.
Remplissez votre canal de compilateur C avec tout cela et prenez une bouffée!
Un compilateur C adapté à cette architecture existe: le C51 de Keil. Mais ce n'est pas bon marché. Il existe également un compilateur open source: SDCC . C'est moyen, mais gratuit. En faisant ce projet, je n'y ai trouvé que deux grands bugs, qui ne pouvaient être surmontés qu'en contournant; ce n'est pas mal du tout pour un projet open source.
Commençons l'analyse
void prvTxBitbang(u8 val)
__naked {
__asm__(
" setb PSW.5 \n"
" jbc _EA, 00004$ \n"
" clr PSW.5 \n"
"00004$: \n"
" clr C \n"
" mov A, DPL \n"
" rlc A \n"
" mov DPL, A \n"
" mov A, #0xff \n"
" rlc A \n"
" mov DPH, A \n"
" mov B, #11 \n"
"00001$: \n"
" mov A, DPH \n"
" rrc A \n"
" mov DPH, A \n"
" mov A, DPL \n"
" rrc A \n"
" mov DPL, A \n"
" jnc 00002$ \n"
" setb _P1_0 \n"
" sjmp 00003$ \n"
"00002$: \n"
" clr _P1_0 \n"
" nop \n"
" nop \n"
"00003$: \n"
" nop \n"
" nop \n"
" nop \n"
" djnz B, 00001$ \n"
" mov C, PSW.5 \n"
" mov _EA, C \n"
" ret \n"
); }
Il est facile de démarrer avec la configuration GPIO. En règle générale, vous rencontrerez plusieurs bits correspondants, qui seront définis ou effacés dans plusieurs registres à la suite. C'est logique, car lors de l'activation ou de la désactivation, vous devez généralement utiliser la broche comme une fonction (à partir du GPIO), la définir comme une entrée ou une sortie, et définir ou lire sa valeur. Vous devriez tomber sur ce type de code au tout début du travail. Voyons ce qu'il y a ... nous constatons que les registres standard
P0
,
P1
et
P2
effectivement utilisés de cette façon, comment traiter les registres GPIO. En regardant quels registres sont écrits autour d'eux et ce qui arrive ensuite aux bits qu'ils contiennent (qu'ils soient lus (entrée) ou écrits (sortie)), nous pouvons supposer que les registres
AD
,
AE
,
AF
Sont conçus pour « la fonction » - et il semble que GPIO, qui sont mis en bits correspondants ne sont pas utilisés comme GPIO, et tout GPIO, en fait utilisé comme GPIO, commencer à travailler seulement après un bit correspondant dans l' un de ces registres sera effacé. Je les ai nommés
PxFUNC
où x est le numéro de port. Ensuite , nous pouvons conclure que
B9
,
BA
,
BB
contrôler la direction. Chaque fois qu'un bit est défini dans l'un d'eux, le GPIO correspondant est uniquement lu, et lorsque le bit est effacé, le GPIO correspondant est en écriture seule. Par conséquent, nous comprenons que ces registres contrôlent la direction du GPIO. Je les ai nommés
PxDIR
où x est le numéro de port. Alors maintenant, en théorie, je pourrais contrôler le GPIO. Si seulement je savais lequel d'entre eux faisait quoi ...
J'ai décidé de les essayer tous d'affilée jusqu'à ce que je trouve celui qui contrôle le "TEST pad" sur la tête de programmation, ou peut-être les pads URX et UTX. Quoi qu'il en soit, en fait ... J'ai trouvé que le port 1 pin 0 (
P1.0
) est "TEST",
P0.6
c'est "UTX", et
P0.7
c'est "URX". Avoir un GPIO contrôlé, vous pouvez vous simplifier la vie, mais seulement tant que vous pouvez gérer le débogage en commutant différents GPIO, et jusqu'à ce que vous en ayez marre. J'ai eu le temps de pratiquer ça!
Nous avons printf!
J'ai utilisé cette fonction pour transformer le pad "TEST" en un port série 8n1 normal en utilisant la méthode bit-bang, et collecté la sortie en utilisant mon analyseur logique. Je l'ai manipulé jusqu'à ce qu'il donne le débit en bauds que mon câble adaptateur USB vers série pouvait gérer. J'avais déjà une implémentation 8051 de printf en assembleur.Pendant une heure, je me suis exercé à sortir des lignes de débogage complexes à partir de ce port série impromptu. Ce n'est certainement pas un mauvais départ, c'est la seule façon dont vous devez agir pour avancer efficacement!
À ce stade, j'ai affiché dans la fenêtre les valeurs de tous
SFR
, pour au moins parcourir ces valeurs. Il y avait encore des problèmes avec des recherches plus poussées. Pour commencer, le chronomètre de surveillance (WDT) semblait être réglé par défaut et réinitialiser la puce après une seconde d'exécution, donc toutes mes expériences devaient tenir en une seconde ou moins. Je ne savais pas encore comment utiliser WDT, alors j'ai supporté cette limitation pendant un certain temps. Quoi qu'il en soit, une seconde, c'est plusieurs cycles!
Élargir l'accès
Maintenant que j'ai pu exécuter le code de manière fiable et afficher les résultats, j'ai décidé de déterminer où se trouvaient les contrôles de graduation. Presque tous les registres ont au moins un registre qui contrôle différentes vitesses (au moins la vitesse du CPU) et un autre registre qui contrôle la fréquence d'horloge (ou réinitialisation) de divers modules. Ils se trouvent généralement comme ceci: le premier est généralement enregistré TRÈS tôt lors du chargement initial, et après cela, il est à peine touché (voire pas du tout). Le second a généralement un bit défini (cycles d'horloge) ou un peu effacé avant de commencer à configurer un périphérique. On ne sait pas où les différents périphériques sont configurés, mais généralement l'ensemble
SFR
avec des nombres similaires correspond à un périphérique. Alors voyons. Certainement il y a un cas, correspondent à cette description:
B7
. Nous voyons qu'il est défini un bit à la fois, avant que plusieurs
SFR
avec des numéros similaires ne soient écrits , et les bits qu'il contient seront effacés après l'
SFR
arrêt des appels à plusieurs avec des numéros similaires. Nous voyons également qu'il est initialement enregistré comme
0x2F
, donc ici nous avons affaire à des périphériques qui sont inclus à l'avance. Étant donné que les bits semblent être définis avant ce que nous considérons comme l'initialisation des périphériques, j'appellerai ce registre
CLKEN
... J'ai joué avec le changement des bits dans ce registre, et il semblait que rien ne se passait lorsqu'ils étaient effacés. En principe, c'est logique, puisque je n'utilise aucun périphérique.
Un autre registre écrit à proximité (le code alphabétisé initialise généralement toutes les opérations d'horloge ensemble), qui n'est alors pas réécrit, est celui-ci
8E
. Il écrit à
0x21
. J'ai suggéré que cela pourrait être lié à la vitesse. J'ai expérimenté. Apparemment, les 4 bits les moins significatifs ne sont en aucun cas reflétés au travail, donc je n'ai aucune idée de la raison pour laquelle ils sont définis
0b0001
, mais les trois bits suivants, probablement, changent la vitesse du processeur de manière assez significative (pour autant que je puisse en juger d'après la vitesse de mon UART, soumis à la dérive). Le bit le plus significatif semblait changer un peu la fréquence, j'ai supposé qu'il était responsable de la commutation entre le circuit RC interne et le cristal externe. Trois bits, qui, je suppose, fonctionnent comme un diviseur de fréquence, définissent la vitesse d'horloge pour qu'elle semble égale
16M / (1 + )
. J'ai nommé ce registre
CLKSPEED
. Par conséquent, la vitesse la plus élevée est atteinte à la valeur
0x01
et la plus basse à
0xf1
Faire fonctionner les minuteries
De nombreux fabricants s'appuient sur toutes sortes de choses dans le 8051, il y a donc très peu de normalisation ici. Cependant, la plupart ne touchent pas l'équipement normal du 8051, tel que le minuteur 0 et le minuteur 1. Veuillez noter: ce n'est pas une règle empirique. Par exemple, TI modifie considérablement les minuteries de ses puces de la série CC. J'ai remarqué que dans cette puce, les registres qui sont normalement censés configurer les minuteries 8051 standard semblent se fermer, et le gestionnaire d'interruption n ° 1 semble également les affecter. Est-ce possible de? Minuteries standard? Je l'ai essayé et cela a fonctionné. Complètement standard, apparemment exactement le même que la spécification d'origine. J'ai vérifié le registre
CLKEN
et j'ai trouvé que le bit 0 (masque
0x01
) pour faire fonctionner les minuteries. Confirmé que le registre standard
IEN0
fonctionne également comme prévu, et que les nombres 1 et 3 entraînent en fait des interruptions pour le temporisateur 0 et le temporisateur 1! Les minuteries semblent fonctionner à exactement 1 / 12ème de 16 MHz, exactement comme on pouvait s'y attendre dans un 8051 standard fonctionnant à 16 MHz. Jusqu'à présent, je n'ai pas trouvé comment changer cette fréquence. Ce que nous savons maintenant registres révèle
TL0
,
TH0
,
TL1
,
TH1
,
TMOD
,
TCON
! Nous avons maintenant des minuteries de précision fonctionnelles!
Je n'ai pas été trop paresseux pour vérifier si la minuterie 2 est réellement implémentée dans le standard 8052 (suite à 8051) Non, ce n'est pas le cas.
Ou peut-être UART?
void uartInit(void) {
//
CLKEN |= 0x20;
//
P0FUNC |= (1 << 6) | (1 << 7);
P0DIR &=~ (1 << 6);
P0DIR |= (1 << 7);
//
UARTBRGH = 0x00;
UARTBRGL = 0x89;
UARTSTA = 0x12;
}
void uartTx(u8 ch) {
while (UARTSTA_1));
UARTSTA_1 = 0;
UARTBUF = ch;
}
Il y avait plusieurs lignes dans le module OTA. Il est logique qu'ils se rapportent à quelque chose, non? Peut-être un port série de débogage? Cela irait bien avec une carte qui a les points clés "UTX" et "URX". Ce code était un peu compliqué, mais il semblait qu'il stockait des octets dans une sorte de tampon. Le code ressemblait définitivement à un tampon en anneau standard. J'ai regardé où ce tampon est en cours de lecture. Il s'est avéré être dans le gestionnaire de l'interruption n ° 0. Oooh, intéressant. Serait-ce un gestionnaire d'interruption UART? Le code semblait vérifier le bit n ° 1 dans une zone qui ressemblait à un registre d'état (registre
98
), et s'il était défini, il lisait un octet de notre tampon en anneau et l'écrivait dans un registre
99
... Si un autre bit (# 0) a été mis à 1 dans le registre d'état susmentionné, alors il lit le registre
99
et insère le résultat dans ... un autre tampon circulaire. Eh bien, c'est sacrément conforme à ce que j'attendais d'un gestionnaire d'interruptions UART! Que faisons-nous ensuite?
Chaque tampon circulaire a deux pointeurs, un pour la lecture et un pour l'écriture. Il est logique qu'ils soient initialisés avant que le tampon ne soit utilisé pour quoi que ce soit. Donc, si nous trouvons où ces indices sont initialisés, alors nous trouverons probablement où l'UART est installé, non? Ressemble vraiment à ça. Dans cette fonction, qui initialise l'UART, on voit que GPIO
P0.6
et
P0.7
mis en mode fonction,
P0.7
est mis sur l'entrée, et
P0.6
- sur la sortie. Deux autres registres:
9A
et
9B
sont écrits avec
0x00
et,
0x89
respectivement. Le registre qui, selon ma version, fonctionne avec des états (registre
98
) est écrit comme
0x10
, puis les bits 0 et 1 sont effacés. Ensuite, le
CLKEN
bit 5 est activé et le
IEN0
bit 0 est activé. C'est, en principe, tout ce dont nous avons besoin!
Nous nommons donc le registre et le registre devient . Nous savons que
99
UARTBUF
98
UARTSTA
UARTSTA
doit être mis à 0x10 pour que ce bloc fonctionne, et nous savons que le bit 0 signifie que l'UART a un octet libre dans la file d'attente TX FIFO, et le bit 1 signifie que l'UART a un octet dans la file d'attente RX FIFO pour nous. Nous savons que le
CLKEN
bit 5 a activé l'horloge pour l'UART et que le numéro d'interruption 0 correspond au gestionnaire d'interruption UART. C'est juste un trésor d'informations. Sachant cela, j'ai pu créer un pilote UART fonctionnel dans mon code et envoyer un message sortant à la broche "UTX" souhaitée, qui, comme nous le savons maintenant, est située au port 0 de la broche 6 (
P0.6
). Nous avons également appris que le point clé "URX" est connecté
P0.7
et qu'il s'agit de la ligne RX dans l'UART. L'UART envoyait des données à 115200 bps, 8n1, et n'était en aucun cas affecté par le registre
CLKSPEED
... Alors, quels sont ces deux autres registres mystérieux qui donnent ces significations magiques?
J'ai essayé de bricoler les deux registres restants,
9A
et
9B
. Il est rapidement devenu clair à quoi ils servaient. Ce sont des diviseurs de fréquence. J'ai branché quelques valeurs pour voir comment elles affectent la vitesse de transmission. Cela s'est avéré simple.
9A
(ci-après dénommé
UARTBRGL
) était l'octet de poids faible, et
9B
(désigné ci-après
UARTBRGH
) était l'octet de poids fort (les 4 bits supérieurs sont apparemment ignorés). Le débit en bauds est calculé simplement comme
16M / (UARTBRGH:UARTBRGL + 1)
. Cela explique parfaitement les valeurs qui semblaient magiques - elles correspondent à 115 200 bauds.
Apparemment, un petit bogue est lié au fait que les bits d'état peuvent être effacés par programme sans affecter le FIFO, donc si vous effacez accidentellement le bit qui signifie "il y a de l'espace libre dans le TX FIFO" (
UARTSTA
.1), alors l'interruption ne se produira jamais et le bit restera bas.
Curieusement, ces emplacements correspondent aux adresses 8051 correctes pour
SCON
et
SBUF
, qui sont les registres de port série 8051. Les bits 0, 1 et 2
UARTSTA
correspondent vraiment aux descriptions
SCON
de 8051, mais c'est là que la similitude est terminée. UART à partir de 8051 nécessite que les bits 7 et 6 soient définis
SCON
en 0 et 1, ce n'est qu'ainsi qu'il deviendra un UART normal. Cette puce dans ce cas nécessite 0 et 0. De plus, l'UART 8051 n'a généralement pas de diviseur de bauds, au lieu duquel le temporisateur 1 est utilisé.
Watchdog timer et "regardez!"
À ce stade, la limite d'exécution de 1 seconde garantie par la configuration du chien de garde par défaut commençait à m'ennuyer. J'ai décidé de savoir où et comment le chien de garde est configuré. En règle générale, le minuteur de surveillance est configuré dans le cadre de sa propre fonction et il est petit. Bien sûr, je ne dirai pas que cela arrive toujours, mais le plus souvent, cela ressemble à ceci. J'avais plusieurs candidats et j'ai essayé de copier de chacun à tour de rôle les écritures des registres dans mon programme de test, mais le chien de garde n'a pas cédé. J'avais besoin de réinitialiser correctement la puce toutes les secondes.
En faisant juste cela, j'ai remarqué une fonction très étrange. Apparemment, elle a lu le registre sous le numéro
FF
, y a écrit quelque chose, puis réinitialisé
P1DIR
, a écrit dans un autre registre, puis a restauré la valeur d'origine dans le registre
FF
. La bizarrerie était que cela définissait TOUTES les broches du port 1 sur la broche. Ça n'a pas de sens. Sur les autres modèles, le port 1 a plusieurs broches configurées comme entrée. De plus, ces registres sont généralement exploités bit par bit, à l'aide d'instructions
anl
(ET logique) et
orl
(OU logique). Une écriture aussi grossière sur l'ensemble du registre semblait à la fois répugnante. Qu'est-ce que le registre
FF
doit être sauvegardé et restauré? Cela avait l'air très étrange!
J'ai décidé d'enquêter. Lors du vidage de la valeur du registre sur la console
FF
, il s'est avéré être zéro, ce qui, bien sûr, ne me convenait pas. J'ai cherché dans tout le micrologiciel et j'ai remarqué que presque partout, il y avait un enregistrement, puis une sauvegarde, puis la valeur d'origine est restaurée. J'ai aussi remarqué que l'écriture se produit presque toujours avec une valeur
0x04
et rarement avec
0x00
... Ce registre a été lu uniquement pendant la sauvegarde pour une restauration ultérieure; aucune autre action n'a été effectuée sur cette valeur. Quelle fonctionnalité cela indique-t-il? En gros, c'est ainsi que les contrôles de banque de mémoire fonctionnent habituellement! Lorsque vous avez plus d'informations que vous ne pouvez en contenir dans votre espace d'adressage, vous devez changer. Ce modèle d'accès (sauvegarde avant modification puis restauration) est typique pour de telles situations pratiques. Mais que peuvent-ils stocker? Pourrait-il être? Ces fous surchargent-ils l'espace mémoire lui
SFR
- même ?!
J'ai écrit un programme qui pouvait afficher les valeurs de tous
SFR
, tous les 128. Puis j'ai transformé le bit
0x04
en
FF
SFR
et a de nouveau pris tout l'espace
SFR
. Ensuite, le programme a encapsulé ce bit et a de nouveau affiché toutes les valeurs. Dieu Tout-Puissant! Et voici! Le bit 2 du registre
FF
économise vraiment de l'espace
SFR
. Je n'ai aucun doute vu que lorsque ce bit est activé, les valeurs qui apparaissent changent. Apparemment, cela n'a pas affecté TOUTES les adresses
SFR
, mais beaucoup. J'ai nommé ce registre
CFGPAGE
.
Maintenant que
CFGPAGE
je pensais que j'étais réglé, je suis retourné à ma fonction mystérieuse, qui a été remise à zéro
P1DIR
. Sachant déjà qu'il n'est PAS remis à zéro dans ce cas
P1DIR
, mais son étrange cousin sur une autre page
SFR
, J'ai essayé de copier ce code dans mon programme. Croyez-le ou non, je suis tombé par hasard sur un code qui désactive WDT !!!
J'ai étudié le code entourant cette fonction, car les fonctions généralement associées dans les binaires sont situées les unes à côté des autres. Il y avait en effet plusieurs fonctions à proximité qui accédaient
CFGPAGE
et accédaient également à l'adresse adjacente
P1DIR
. Après quelques heures d'essais et d'erreurs, j'ai parfaitement compris les détails du fonctionnement du chien de garde. Sur la 4ème page des configurations, l'adresse
BF
apparaît pour contrôler l'activation et la réinitialisation du temporisateur de surveillance; le bit le plus significatif de ce registre active ou désactive la fonction de réinitialisation de puce dans le temporisateur de surveillance. Je l'ai nommé
WDTCONF
. Adresse
BA
(qui se trouve
P1DIR
sur la page de configuration 0) est le registre d'activation du chien de garde. Le bit 0 active ou désactive ici le temporisateur du chien de garde lui-même. Je l'ai nommé
WDTENA
.
Jusqu'à ce point, je cherchais toujours à apprivoiser le chronomètre du chien de garde. Cela a pris un certain temps, mais à la fin je l'ai compris. Un registre
BB
(maintenant nommé
WDTPET
) peut être écrit à zéro pour apprivoiser la minuterie du chien de garde. Il m'a fallu quelques minutes de plus pour comprendre comment configurer le délai dans la minuterie du chien de garde, car il y avait clairement un trou dans l'espace d'adressage entre
BB
et
BF
... Le compteur a une longueur de 24 bits et est surchargé lorsqu'il est apprivoisé. Il ne peut pas être lu. Valeur Recharger enregistrée dans
WDTRSTVALH
:
WDTRSTVALM
:
WDTRSTVALL
, situé à
BE
,
BD
,
BC
respectivement, sur la page de configuration 4. Le compteur de compte UP à une fréquence d'environ 62 kHz, et un trop - plein est déclenché. Ainsi, afin de délivrer un retard accru, une valeur plus petite doit être écrite dans ces registres de réinitialisation.
Des possibilités plus subtiles
Programmation de la mémoire flash
// irqs
voif flashDo(void) {
TRIGGER |= 8;
while (!(TCON2 & 0x08));
TCON2 &=~ 0x48;
SETTINGS &=~ 0x10;
}
void flashWrite(u8 pgNo, u16 ofst,
void *src, u16 len) {
u8 cfgPg, speed;
speed = CLKSPEED;
CLKSPEED = 0x21;
cfgPg = CFGPAGE;
CFGPAGE = 4;
SETTINGS = 0x18;
FWRTHREE = 3;
FPGNO = pgNo;
FWRDSTL = ofst;
FWRDSTH = ofst >> 8;
FWRLENL = len - 1;
FWRLENH = (len - 1) >> 8;
FWRSRCL = (u8)src;
FWRSRCH = ((u16)src) >> 8;
flashDo();
CFGPAGE = cfgPg;
CLKSPEED = speed;
}
void flashRead(u8 pgNo, u16 ofst,
void __xdata *dst, u16 len) {
u8 pgNo, cfgPg, speed;
speed = CLKSPEED;
CLKSPEED = 0x21;
cfgPg = CFGPAGE;
CFGPAGE = 4;
SETTINGS = 0x8;
FWRTHREE = 3;
FPGNO = pgNo;
FWRDSTL = (u8)dst;
FWRDSTH = ((u16)dst) >> 8;
FWRSRCL = ofst;
FWRSRCH = ofst >> 8;
FWRLENL = len - 1;
FWRLENH = (len - 1) >> 8;
flashDo();
CFGPAGE = cfgPg;
CLKSPEED = speed;
}
void flashErase(u8 pgNo) {
u8 __xdata dummy = 0xff;
u8 cfgPg, speed;
speed = CLKSPEED;
CLKSPEED = 0x21;
cfgPg = CFGPAGE;
CFGPAGE = 4;
SETTINGS |= 0x38;
FWRTHREE = 3;
FPGNO = pgNo;
FWRDSTL = 0;
FWRDSTH = 0;
FWRLENL = 0;
FWRLENH = 0;
FWRSRCL = (u8)&dummy;
FWRSRCH = ((u16)&dummy) >> 8;
flashDo();
CFGPAGE = cfgPg;
CLKSPEED = speed;
}
Je me suis concentré sur l'image OTA car elle est plus petite que le firmware principal. Un détail qui est absolument nécessaire dans l'image OTA est la possibilité d'écrire dans la mémoire flash. À quoi cela ressemble-t-il? On suppose que nous avons besoin d'une sorte de fonction qui effacera le flash, car le flash est effacé par blocs. Vous avez également besoin d'une fonction d'écriture qui peut écrire une page de données ou moins. Nous avons besoin d'une sorte de vérification des données enregistrées. Le seul détail qui diffère dans les implémentations est la façon dont nous alimenterons les données destinées à l'écriture sur le contrôleur flash. Je ne savais pas à quoi cela devrait ressembler, mais le reste était assez facile à trouver. La vérification se résumerait probablement à un simple appel
memcmp
ou cycle. Les opérations d'effacement du flash épuisent la mémoire flash, la page doit donc être vérifiée avant l'effacement, puis l'opération effectuée.
À la recherche d'un contrôle de pré-effacement, j'ai rapidement trouvé une fonction qui crée une zone d'
0x400
octet à
XRAM
plein d'octets
0xFF
. Ensuite, la zone de mémoire est
CODE
comparée à ce tampon, et si elles ne sont pas égales, les interruptions sont désactivées, et certaines sont touchées
SFR
sur la page de configuration 4. La taille de la page en mémoire flash est clairement de 1024 octets. Vérifier quels autres endroits sont affectés par la même chose
SFR
, nous trouvons le code flash restant. Le contexte montre clairement ce que font ces registres et comment. Dans ce cas, il est intéressant de savoir comment les données sont transmises à l'unité de commande de mémoire flash. Ce bloc de contrôle contient clairement un bloc DMA. Une adresse est fournie à l'unité de commande de mémoire flash
XDATA
et les données en sont directement absorbées. C'est cool!
À ce moment-là, je ne savais pas encore comment lire INFOBLOCK. Apparemment, le code OTA ne le concernait pas, mais de quelque part il DOIT être lu - après tout, il contient des données. J'ai vérifié l'image principale et j'ai remarqué un extrait de code affectant la même chose
SFR
à partir de la mémoire flash, mais d'une manière différente. Avec quelques analyses supplémentaires, j'ai pu reproduire la lecture correcte d'INFOBLOCK. Il est curieux que la même méthode puisse être utilisée pour lire n'importe quel autre bloc de mémoire flash, mais il n'est pas nécessaire de le faire, car tout ce que vous avez à faire pour lire la mémoire flash est de lire la zone de mémoire
CODE
. INFOBLOCK n'est accessible que via l'unité de commande de la mémoire flash. Pour l'écriture et la lecture à partir de la mémoire flash, le bloc de contrôle utilise l'accès direct à la mémoire (DMA) et écrit dans
XDATA
.
Un registre
DF
(
FWRTHREE
) a défié toute tentative de l'expliquer. Il a toujours eu un record avec la valeur
0x03
, Je ne sais pas pourquoi. Mon code d'accès flash fait de même. Register
D8
(
FPGNO
) est écrit avec le numéro de page flash. Les pages principales de la mémoire flash sont numérotées de 0 à 63, INFOBLOCK a le numéro 128
DA
.:
D9
(
FWRSRCH
:)
FWRSRCL
est la source du bloc DMA dans le bloc de contrôle de la mémoire flash. Pour écrire sur flash, il contient l'adresse
XDATA
où l'on trouve les données à écrire. Pour lire le flash, un décalage d'octet sur la page d'origine est recherché et la lecture commence à ce décalage.
DC
:
DB
(
FWRDSTH
:
FWRDSTL
) Est l'affectation pour DMA dans le bloc de gestion de la mémoire flash. Pour l'écriture sur flash, il contiendra le décalage d'octet sur la page de destination, et l'écriture commencera à partir de ce point. Pour lire le flash, on utilise l'adresse
XDATA
à laquelle les données reçues lors de la lecture sont écrites.
DE
:
DD
(
FWRLENH
:)
FWRLENL
Est la longueur des données que le bloc DMA doit transférer, moins un.
L'écriture dans la mémoire flash en tant que telle est déclenchée en définissant un bit dans un de plus
SFR
. Divers bits qu'il contient sont également définis pour contrôler d'autres codes, apparemment non liés à la mémoire flash, j'ai donc conclu que ce registre déclencherait probablement diverses actions. J'ai nommé ce registre
D7
sur la page de configuration 4
TRIGGER
. L'état d'achèvement est également vérifié dans un registre qui semble également être partagé par un autre code.
CF
J'ai nommé ce registre à partir de la page de configuration 4
TCON2
, pourquoi pas? Il y avait aussi un registre sur
C7
, également utilisé en conjonction avec un autre code, qui configurait apparemment l'opération à effectuer. Je l'ai nommé
SETTINGS
.
0x30
y a été écrit avec un OU logique pour effacer + écrire,
0x18
pour écrire un flash,
0x08
pour lire un flash. J'ai deviné que le bit
0x08
signifie "transfert de données en attente"
0x10
signifie "en flash", et
0x20
"Effacer". Cela a du sens compte tenu des valeurs que nous voyons et des opérations effectuées ici.
La lecture et l'écriture sur le flash fonctionnaient à merveille, mais l'effacement ne fonctionnait apparemment pas. Au lieu d'effacer la page avec le code donné, pour une raison quelconque, la page sur laquelle se trouvait le code demandant l'effacement était effacée en permanence. De toute évidence, ce problème n'était pas dans le code qui était contenu sur cet appareil, je faisais quelque chose de mal. Vérifié, vérifié et vérifié à nouveau pour m'assurer que mon code correspond au code d'usine. Matched. Qu'est-ce qui ne va pas? J'ai travaillé pendant plusieurs jours jusqu'à ce que je réalise que le code d'usine fonctionne à 4 MHz et le mien à 16 MHz. Cela pourrait-il être le point? Il s'est avéré exactement ainsi! J'ai changé mon code d'effacement du flash pour conserver le diviseur de fréquence actuel et j'ai ralenti l'horloge à 4 MHz pendant la durée de l'effacement du flash. Cela s'est bien passé car ce code est déjà en cours d'exécution avec les interruptions désactivées.
Une autre subtilité de cette unité de commande de mémoire flash est qu'elle ne prévoit apparemment pas une simple opération "d'effacement". J'ai pensé à attribuer les bits if appropriés dans le registre
SETTINGS
, puis il m'a semblé logique que lorsqu'il était réglé sur
0x20
ou
0x30
, un simple effacement devait se produire. La seule façon d'effacer est d'effectuer une effacement + opération d'écriture, qui écrit au moins un octet (car il n'y a aucun moyen de représenter une longueur nulle dans
FWRLENH
:.
FWRLENL
Pour effectuer un effacement simple, je demande simplement d'écrire un seul octet
0xFF
. Ça marche
SPI
Fondamentalement, tous les pilotes SPI sont identiques. Un octet est reçu en entrée, un octet est renvoyé en sortie. Bien sûr, certains ont DMA et certains sont pilotés par interruption, mais 99% d'entre eux dans les petits systèmes sont contrôlés par logiciel, et quelque part il y a une fonction simple
u8 spiByte(u8 byte);
.
Il était logique d'approfondir SPI. Puisque nous savons qu'il
SSD1623L2
communique avec SPI, et que nous connaissons également les détails de l'organisation d'une telle communication, nous avons juste besoin de regarder le code et de savoir quelle partie de celui-ci devrait effectuer cette opération. Tout comme Sudoku, étant donné ce que nous savons déjà, cette recherche ne sera pas difficile. En regardant la fiche technique
SSD1623L2
on voit que le numéro de registre du premier octet envoyé est écrit dans les bits 1..6, et le bit "écriture" est à la position # 7. Tous les registres ont une longueur de 24 bits. Il est logique que le programmeur écrive un code qui prendra le numéro de registre comme paramètre, le décalant vers la gauche de un, éventuellement logique-ou-entrée
0x80
, si une écriture est demandée, puis transférera trois octets. Tous les programmeurs n'agissent pas de manière logique, mais cette hypothèse aide énormément à la rétro-ingénierie. En regardant le code, il est facile de voir les fonctions qui semblent faire exactement cela. Certains ajoutent
0x80
, d'autres non. Ils appellent tous cette même fonction mystérieuse pour chaque octet. Donc, nous supposons que certains affichent du texte à l'écran, d'autres le lisent. Abordons la fonction mystérieuse elle-même.
En fait, tout est aussi simple que de décortiquer des poires ici. Il passe
CFGPAGE
à 4, puis écrit la
ED
valeur dans le registre
0x81
, écrit l'octet à envoyer
EE
, écrit
0xA0
dans
EC
, effectue un retard de 12 microsecondes, met le bit 3 à
EB
, lit l'octet reçu à partir de
EF
, stocke
0x80
dans
ED
. C'est tout. Comment comprendre tout cela? Comme avant, en s'appuyant sur ce qui est déjà connu.
0x80
et
0x81
diffèrent par un seul bit, et nous le définissons avant de commencer l'opération SPI, et après la fin du travail, nous le réinitialisons, donc c'est, apparemment, un bit "d'activation" d'une certaine sorte. D'un autre côté, le sens sonne
0xA0
littéralement comme une configuration quelconque. Le registre
EB
est encore un mystère. Mais, si je reproduis ce code sans y écrire, tout fonctionnera, alors j'en conclus que peu dépend de ce registre. Certainement
EE
ceci
SPITX
et
EF
cela
SPIRX
. J'ai appelé
ED
-
SPIENA
et
EC
-
SPICFG
.
Reste à caractériser ce que font les beats
SPICFG
... J'ai fait un peu d'essais et d'erreurs, armé d'un analyseur logique. Le bit 7 doit être mis à 1, le bit 6 doit être effacé. Le bit 5 démarre la transmission de l'octet SPI et s'efface lorsqu'il en a terminé avec lui. Les bits 3 et 4 définissent la fréquence d'horloge, vous pouvez choisir parmi les valeurs: 500KHz, 1MHz, 2MHz, 4MHz. 2 est le bit de configuration standard
CPHA
pour SPI, le bit 1 est
CPOL
. Le bit 0 semble violer RX. Je suppose qu'il peut configurer le bloc pour le semi-duplex (en ligne
MOSI
). En général, ce n'est pas si difficile.
Pin par pin, trouvez rapidement la configuration GPIO et voyez ce que
P0.0
c'est
SCLK
,
P0.1
ceci
MOSI
et
P0.2
cela
MISO
... En cherchant où ces GPIO sont configurés, nous voyons également comment le bit
CLKEN
SPI est nécessaire : c'est le bit 3. Génial - nous avons maintenant un SPI qui fonctionne!
Déterminez la température
volatile u8 __xdata mTempRet[2];
void TEMP_ISR(void) __interrupt (10)
{
uint8_t i;
i = CFGPAGE;
CFGPAGE = 4;
mTempRet[0] = TEMPRETH;
mTempRet[1] = TEMPRETL;
CFGPAGE = i;
IEN1 &=~ 0x10;
}
int16_t tempGet(void)
{
u16 temp, sum = 0;
u8 i;
CLKEN |= 0x80;
i = CFGPAGE;
CFGPAGE = 4;
TEMPCFG = 0x81;
TEMPCAL2 = 0x22;
TEMPCAL1 = 0x55;
TEMPCAL4 = 0;
TEMPCAL3 = 0;
TEMPCAL6 = 3;
TEMPCAL5 = 0xff;
TEMPCFG &=~ 0x08;
CFGPAGE = i;
IEN1 &=~ 0x10;
for (i = 0; i < 9; i++) {
//
IEN1 |= 0x10;
//
while (IEN1 & 0x10);
if (i) { //
sum += u8Bitswap(mTempRet[0]) << 2;
if (mTempRet[1] & 1)
sum += 2;
if (mTempRet[1] & 2)
sum += 1;
}
timerDelay(TICKS_PER_S / 1000);
}
//
CLKEN &=~ 0x80;
return sum / 8;
}
E-Ink affiche les mises à jour différemment en fonction de la température actuelle, il est donc essentiel de connaître la température ambiante pour les mettre à jour correctement. Les formes d'onde correctes sont sélectionnées en fonction de la température. Ici, la connaissance de l'extérieur sera utile. Donc, si nous pouvons trouver où les formes d'onde sont chargées dans le contrôleur d'affichage, nous pouvons trouver où les choix sont faits. De cet endroit, vous pouvez marcher directement jusqu'au point où la température est mesurée, non? Cela fait, nous allons à exactement une fonction, dont la sortie détermine la forme d'onde qui sera utilisée. Ça doit être ça! À propos: généralement, les capteurs de température sont connectés à l'ADC - presque personne ne les fabrique dans une version séparée. Mais cela n'a pas d'importance [encore].
Tout commence par régler le bit 7 sur
CLKEN
et se termine par sa réinitialisation, de sorte qu'au moins nous sachions que c'est ainsi que nous allumons et éteignons le capteur de température (ou ADC). La fonction passe
CFGPAGE
à 4, puis écrit une série de valeurs dans une série de registres. Toutes les valeurs sont constantes.
0x81
-> reg.
F7
,
0x22
-> reg.
E7
,
0x55
-> reg.
E6
,
0x00
-> reg.
FC
,
0x00
-> reg.
FB
,
0x03
-> reg.
FE
,
0xFF
-> reg.
FD
, puis les bits sont
0x81
vidés
F7
. Après
CFGPAGE
récupère puis efface le bit 4 du registre
A1
. Cela semble être la configuration initiale. Une fois qu'une certaine procédure se produit cinq fois, les résultats de toutes les opérations sauf la première sont moyennés. Après cela, beaucoup de calculs sont effectués sur la moyenne obtenue de cette manière, en particulier en utilisant les valeurs d'INFOBLOCK - ce sont probablement des valeurs d'étalonnage. Le résultat est ensuite renvoyé. Regardons de plus près les détails.
Dans le processus, le bit 4 du registre a simplement été défini
A1
, le bit global a été mis à 1, puis en mode veille actif, nous passons du temps jusqu'à ce que le bit soit effacé. Les valeurs moyennes spécifiques, apparemment, sont tirées de quelque valeur globale. C'est bizarre ... J'ai cherché où il est écrit et je l'ai trouvé dans le gestionnaire d'interruption # 10. Apparemment, c'est ainsi que le bit 4 du registre a été effacé
A1
, puis le passage à la page de configuration 4 a eu lieu, les valeurs ont été lues à partir des registres
F8
et
F9
, et des choses étranges ont été faites avec eux, puis cette valeur globale a été écrite . Mais que fait-on de ces valeurs?
J'étais juste aux yeux des constantes piquées
0x55
,
0xAA
,
0xCC
et
0x33
... Est-ce possible? Quelqu'un pourrait-il être si direct que ... eh bien, oui. Ce sont des constantes pour une manière intelligente d'inverser l'ordre des bits dans un octet. Tricky, mais uniquement sur des processeurs plus avancés. Sur 8051, cette approche est très inefficace. Mais pourquoi? Il semble que quelle que soit l'IP (pointeur de commande) qu'ils autorisent à mesurer la température, cela produit un résultat dans lequel les bits sont dans l'ordre inverse. Pourquoi ce problème devrait être résolu au niveau logiciel d'une puce propriétaire est une grande question. Après tout, inverser l'ordre des bits dans le matériel n'est pas plus difficile que de réorganiser quelques fils ... Que fait-il? Je ne sais pas. En fait, je ne l'ai jamais compris.
Presque personne ne conçoit un compteur de commandes dédié pour un capteur de température, cette chose est simplement branchée sur l'ADC. Une fois que j'ai pu réimplémenter ce code et m'assurer qu'il fonctionnait très bien, j'ai essayé de changer tous ces registres. La plupart d'entre eux ont influencé le gain du capteur de température, certains n'ont eu aucun effet. S'il s'agissait d'un ADC normal, nous nous attendrions à ce que certains bits le basculent vers un type d'entrée différent et donnent une valeur complètement différente. Malheureusement, cela ne s'est pas produit. Cela ressemblait vraiment à un capteur de température normal. Ceci est également confirmé car ces registres ne sont touchés nulle part ailleurs. Bizarre comme l'enfer, mais d'accord ...
Étant donné que presque tous ces registres ne sont écrits qu'une seule fois, et que ce sont ces valeurs et que leur modification affecte la valeur mesurée, j'ai décidé de les appeler simplement toutes les valeurs d'étalonnage de la température. Par conséquent, nous nous familiarisons avec
TEMPCAL1
(reg.
E6
),
TEMPCAL2
(Reg.
E7
),
TEMPCAL3
(Reg.
FB
),
TEMPCAL4
(Reg.
FC
),
TEMPCAL5
(Reg.
FD
) Et
TEMPCAL6
(reg.
FE
). Je l'ai nommé car il est utilisé plusieurs fois et semble en fait gérer le chargement de la valeur d'étalonnage. Les résultats sont publiés en (reg.
F7
TEMPCFG
TEMPRETH
F8
) et
TEMPRETL
(reg.
F9
). Les résultats ont une longueur de 10 bits, alignés sur l'extrémité supérieure d'un registre de résultats de 16 bits, avec un ordre de bits inversé.
J'ai également remarqué que le bit 3 est
TEMPCFG
défini lorsque la création de l'échantillon est terminée. Curieusement, le code d'usine ne le vérifie pas, s'appuyant plutôt sur l'interruption. Mais, en fait, cela s'est avéré utile pour décoder le but du registre
A1
. Comme vous pouvez le voir, le 8051 classique est limité à 7 sources d'interruption, puisque nous avons 8 bits dans le registre
IEN
et le bit 7 est réservé pour activer une interruption globale. Alors, comment gérez-vous les interruptions numérotées 7 et plus? En fait, c’est comme le Far West, ce que vous voulez, c’est ce que vous faites. Mais ici, nous avons un morceau de matériel qui déclenche l'interruption numéro 10, et en utilisant un peu, nous pouvons dire quand cela a été fait. C'est génial pour expérimenter. dans lequel nous voulons savoir comment les interruptions au-dessus de 7 sont activées et désactivées. Il était juste nécessaire de bricoler ce code jusqu'à ce que vous vous débarrassiez de l'interruption, mais l'exemple est créé . La recherche n'a pas duré longtemps. Ça doit être ça
A1
! Je l'ai nommé
IEN1
... Je ne sais pas quelle est la fonction du bit 0 ici, mais les bits 1 et plus contrôlent l'activation des interruptions numéro 7 et plus. J'ai pu le confirmer plus tard. D'accord, c'est fait - nous avons documenté encore un autre périphérique, découvrant ainsi encore plus de bizarreries ...
I2C
À ce stade, j'ai ouvert une plus grande étiquette de prix e-Ink équipée de la même puce. C'était un modèle de 2,9 pouces avec un écran graphique e-ink et NFC !!! Encore une fois, la connaissance tierce est utile ici. La plupart des appareils NFC vous diront exactement ce qu'ils sont si vous le demandez poliment. C'est une bonne chose, car la puce NFC sur la carte était trop petite pour être correctement étiquetée. Après l'avoir numérisé en utilisant NFC et vérifié l'ID de l'appareil, nous découvrons qu'il s'agit de NXP NT3H1101 (copie archivée ici pour la postérité). Depuis cette page très pratique, vous pouvez télécharger la fiche technique - et il devient immédiatement clair comment la communication avec cette puce doit se dérouler. Information utile! (Toutes les informations sont utiles ici). La seule chose ennuyeuse est que l'adresse I2C de cet appareil n'est pas fixe, mais elle peut être définie sur n'importe quelle valeur; cependant, une valeur par défaut est fournie. L'alphabet du reverse engineering: dans 99,9% des cas, les valeurs par défaut ne changent pas. Je parie que l'adresse I2C par défaut n'a pas changé non plus!
Trouver un analogue binaire pour est
0x55
assez facile - cette valeur n'est pas si courante. Apparemment, ils sont tous effectués avant les appels à l'une des deux fonctions. Il est logique qu'ils soient connectés à I2C. De plus, dans tous les cas, avant ces appels, le bit 4 est mis à
CLKEN
qui est ensuite jeté. Nous savons maintenant que I2C est activé via ce bit. Jetons un coup d'œil à ce que font ces fonctions. Certains copient les données du paramètre fourni au tout début, d'autres le font à la fin. Au milieu, ils écrivent tous des trucs globaux, définissent le bit global, effacent le bit 4, et mettent le bit 5 dans le registre
95
et attendent qu'il soit effacé. Hmm, fonctionne comme un capteur de température. Apparemment, le bit 2 de l'
IEN1
interruption s'active.
Voyons où se trouve le gestionnaire d'interruption qui affecte ces valeurs globales. En effet, son numéro d'interruption est 8, comme prévu. Il met
CFGPAGE
à 0 puis lit le registre
91
... Les 3 bits les moins significatifs sont ignorés et les bits restants sont utilisés dans le boîtier de commutation pour décider quoi faire. Ce code s'est avéré un peu déroutant, j'ai donc décidé d'expérimenter. Attaché l'analyseur logique aux lignes allant à la puce NFC et rapidement trouvé où
SDA
et où
SCL
. C'était facile car il existe une fiche technique pour cette puce.
Il semble que l'effacement du bit 4 dans le registre
95
n'affectera rien, mais le réglage du bit 5 fait que la condition START sur le bus est vraie. Une interruption est déclenchée. Si vous faites de même en utilisant le gestionnaire intégré et lisez les 5 bits les plus significatifs du registre
91
, nous voyons qu'ils ont une valeur
0x08
... L'octet d'adresse est ensuite stocké avec le
94
bit R / W (lecture / écriture) dans le registre , et le bit 3 dans le registre est effacé
95
. Il convient également de noter que TOUS les chemins à travers ce gestionnaire d'interruption entraînent l'effacement du bit 3 dans le registre
95
. Je suppose que c'est le "morceau qui doit être interrompu". Je ne l'ai pas encore compris, mais nous pouvons déjà nommer certains registres. Il semble que tous les registres I2C sont sur la page de configuration 0.
Je vais appeler car c'est I2C qu'il contient et n'est jamais lu pour aucune autre raison. Je n'ai jamais vu les trois bits les moins significatifs changer ou être utilisés de quelque manière que ce soit. - alors je vais appeler
91
I2CSTATE
I2CBUF
94
, puisque les données y sont pompées le long du convoyeur et
95
qu'elles seront nommées à l'avenir
I2CCTL
, car pour que les choses soient faites, il faut y écrire quelque chose.
Nous creusons plus loin et constatons que lorsque l'octet d'adresse est envoyé, l'une des quatre valeurs d'état peut être obtenue. Si l'octet d'adresse que nous avons envoyé nécessitait un accès en écriture, l'état sera
0x18
s'il a été acquitté (ACK), et
0x20
sinon. Si l'octet d'adresse que nous avons envoyé nécessitait un accès en lecture, l'état sera
0x40
s'il a été acquitté (ACK), et
0x48
sinon. La gestion de NAK (octet non acquitté) est assez simple. Lorsque le bit 5 est réglé sur
I2CCTL
la condition STOP sur le bus est vraie.
L'envoi de données en mode écriture est facile. L'octet est simplement écrit
I2CBUF
. Si l'octet envoyé est acquitté (ACK), alors l'état deviendra,
0x28
et sinon, alors
0x30
. Pour provoquer un redémarrage, réglez le bit 4 sur
I2CCTL
- cela fonctionne. Lorsque l'exécution de la commande RESTART sur le bus est terminée, l'état devient
0x10
.
Si nous voulons lire les informations, alors, après l'envoi du bit de redémarrage et de l'octet d'adresse en mode lecture, dès que nous voyons l'état
0x40
, nous pouvons décider comment répondre à l'octet suivant que nous recevons - ACK ou NAK. Pour l'acquitter (ACK), réglez le bit 2 sur
I2CCTL
, et afin de ne pas confirmer (NAK) - nous effaçons ce bit. Avec le retour du gestionnaire, l'octet sera reçu. Lorsque cela est fait, nous verrons l'état
0x50
si l'octet a été confirmé, et
0x58
s'il n'a pas été confirmé. D'une manière ou d'une autre, l'
I2CBUF
octet reçu sera contenu dans.
Après avoir examiné le code d'initialisation et bricolé notre copie, nous constatons que le bit 7 de
I2CCTL
contrôle si le périphérique déclenchera des interruptions. Sinon, ce registre est initialisé à
0x43
... Je suppose que c'est ainsi que le bloc est configuré pour fonctionner en mode maître. Comme je n'ai pas d'exemple de code pour le mode esclave, je n'ai pas approfondi cette question, mais je suis sûr que le mode esclave est pris en charge. Cela peut être fait, mais je suis paresseux :).
Le registre a
96
également enregistré des informations dans le temps d'initialisation, puis ne change plus. Cela correspond bien à un bit d'information qui nous manque encore - indiquant comment la vitesse d'horloge est réglée. Après avoir expérimenté ce registre (qui s'appelle maintenant
I2CSPEED
), nous voyons qu'il a une interdépendance complexe avec la fréquence d'horloge, mais après plusieurs dizaines de tentatives, je suis arrivé à ce qui suit:
rate = 16MHz / ((dividerB ? 10 * (1 + dividerB) : 12) << dividerA)
où dividerA est les trois bits les moins significatifs
I2CSPEED
et dividerB est le 4 suivant. Le bit le plus significatif n'est apparemment pas utilisé.
Le fait que la configuration GPIO initiale se produise près du point d'initialisation du périphérique semble impliquer que les broches
P1.4
et sont importantes dans ce cas
P1.5
.
Tout fonctionnait, mais il y avait un secret. Lorsque l'interruption de ce bloc a été activée (c
IEN1
), le bit 2 a également été mis à 1 dans le registre
A2
. Comme il
IEN1
se trouve à l'adresse
A1
, je soupçonne que cela a à voir avec une interruption. Je n'ai toujours pas compris exactement ce qu'il fait, et aucun code autre que le code de configuration I2C initial ne l'utilise. Je l'ai déjà nommé
I2CUNKNOWN
bien qu'il soit plus susceptible d'être lié à une interruption qu'à l'I2C. Quoi qu'il en soit, mon code peut désormais effectuer des transactions I2C en tant que maître!
Détection de changement de broche
Le micrologiciel de l'étiquette de prix a été réveillé lorsqu'il a été scanné par un appareil compatible NFC. La puce NFC intégrée a une broche de "détection de champ" connectée au microcontrôleur principal. Coïncidence? Paspense! Il doit y avoir un moyen de détecter les changements sur la broche. Il réveille même la puce du mode veille (économie d'énergie). De plus, il faut un certain temps pour dessiner avec de l'encre électronique, et pendant cette attente, la puce devrait probablement continuer à dormir. L'écran signalera la fin du dessin en changeant le signal "OCCUPE". Donc ... nous avons deux cas dans lesquels le CPU doit détecter un changement sur une broche et, très probablement, nous ne parlons pas d'un cycle d'attente actif. Il serait difficile de trouver le premier cas décrit - je ne savais toujours pas exactement où se trouve ce code d'hibernation. Le deuxième cas, au contraire, était très facile à trouver - je veux dire, il était facile de trouver le code pour dessiner sur l'écran. Là encore, il est utile de s'appuyer sur les connaissances existantes. Je savais,quelle équipe est chargée de «rafraîchir l'écran» sur pratiquement toutes les puces d'affichage à encre électronique existantes. Je suis juste entré et j'ai vu ce qui allait se passer. Il y avait beaucoup de code, beaucoup ont été touchés
SFR
... J'ai commencé à expérimenter avec les quelques-uns que j'ai vus. Faites quelques suppositions éclairées: toutes les broches devraient pouvoir déclencher la détection de changement. Ce n'est pas toujours le cas, mais une estimation éclairée est généralement tirée. J'ai supposé que quels que soient les registres de configuration dont nous parlions, ils seraient séquentiels et fonctionneraient avec trois ports. J'ai également supposé que changer la broche devrait fournir une interruption, et pas seulement réveiller l'appareil. Il est logique que le nombre de registres de configuration soit assez prévisible. Pour chaque broche, nous avons besoin ENABLE, STATUS et, très probablement, DIRECTION. De plus, les registres liés à la détection de changement GPIO sont susceptibles de se trouver à proximité d'autres registres de configuration GPIO.
Sur cette base, j'ai fait quelques expériences, car je pouvais facilement changer au moins certaines des broches (par exemple, TEST). Il a également fallu du temps pour voir comment ma carte actuelle se développe
SFR
. Je n'ai pas oublié de regarder les registres
BC
,
BD
et
BE
sur la page de configuration 0. Plusieurs expériences ont montré qu'ils contrôlaient le pullup de chaque broche. Certes, je n'ai jamais vu de configurations qui permettraient de "tirer la goupille vers le bas". Je les ai nommés
PxPULL
.
Après plusieurs expériences, il est devenu clair qu'il y avait trois registres par port, et ils contrôlent les interruptions lorsque la broche change.
PxLVLSEL
(
A3
,
A4
,
A4
) sélectionne le niveau souhaité (0 = haut, 1 = bas).
PxINTEN
(
A6
,
A7
,
A9
) Fournit broche de suivi des modifications au niveau du matériel.
PxCHSTA
(
AA
,
AB
,
AC
) Enregistre le statut de détection (bit = ensemble quelque chose a changé). D'autres expériences ont montré que le numéro d'interruption lors du changement de la broche est 11. Fonctionne bien, et j'ai même réussi à réveiller la puce du mode d'économie d'énergie (plus à ce sujet ci-dessous).
Deuxième DPTR
Enregistre
84
et
85
enregistre mystérieusement au milieu de toutes les transactions d'échange
CFGPAGE
et conserve les 8 bits stockés en eux. Dans de nombreuses variantes du 8051, c'est là que devrait se trouver le deuxième registre
DPTR
. Mais, si oui, comment y passer ? Tout le monde le fait différemment. J'ai décidé de l'essayer. Écrit un programme dans l'assembleur pour inverser tour à tour chaque bit de chaque registre et vérifier si l'écriture d'un entier
DPTR
(instruction spéciale) correspond à la lecture suivante
DPL
et
DPH
(accès normal à
SFR
). Il est prévisible que beaucoup de ces choses ne peuvent pas être changées si facilement sans faire planter le programme. Mais, après m'être entraîné soigneusement à sauter l'un ou l'autre, j'ai isolé le bit 0
92
. Eh bien, oui ... C'est ce qu'il fait. Comme pour beaucoup de 8051, j'ai nommé ce registre
DPS
, ce qui signifie "sélection de pointeur de données". Registers
84
et
85
j'ai nommé, naturellement,
DPL1
et
DPH1
.
Autres expériences.
Certaines expériences ont montré que les deux bits les moins significatifs en
PCON
(veille et veille) fonctionnent comme prévu en mode veille pour le 8051 (bien que le mode veille en mode économie d'énergie puisse également être configuré). J'ai également noté que le réglage du bit 4 est désactivé
XRAM
. Cela économise un peu plus d'énergie en mode veille!
Les registres de la gamme
B2
.. sont intéressants
B6
. Ils semblent varier en fonction des instructions suivies à leur emplacement. Après avoir tout examiné attentivement, je me suis rendu compte que
B4
:
B5
il est toujours à jour
PC
!!! Pourquoi quelqu'un pourrait en avoir besoin - je ne sais pas. Nommé les
PCH
et
PCL
... Ils sont en lecture seule. Mais qu'en est-il des autres registres de cette gamme?
B2
et
B3
semble être associé à des sauts conditionnels. Lors d'un saut en longueur (par exemple, en courant
ljmp
,
lcall
ou
ret
), ils semblent stocker la destination du saut. Avec de courtes transitions (telles que
sjmp
),
B2
il semble comprendre le déplacement. Des choses étranges, mais inutiles, alors je ne les ai pas approfondies. J'ai nommé le reste des registres
PERFMONx
.
Dormez en mode économie d'énergie
Les gens sont des gens et rien d'humain ne leur est étranger. Les gens aiment les nombres ronds. J'aime la précision, même si je n'en ai pas besoin. Cela aide beaucoup avec la rétro-ingénierie. Par exemple, comment répondez-vous à une constante
0x380000
? Rien? Peut-être. Et pourquoi pas
0x36EE80
? Les yeux se collent déjà à elle. Bon sang qu'est-ce que ça signifie? Traduisez-le en système décimal et vous voyez: 3 600 000. Eh bien , c'est une heure, exprimée en millisecondes. Cette valeur ne peut être utile, peut-être, que dans le cas d'un long sommeil en mode économie d'énergie. J’en ai marre de compter le nombre de choses que j’ai «rétro-ingénierie» en me basant sur des constantes de ce genre qui jettent la lumière sur l’endroit où le rêve se réalise!
Voici les constantes sur cet appareil qui ont été passées à la fonction qui m'intéresse: 1 5000 2 000 5 000 10 000 3 600 000 1 800 000 0xffffffff. Il est tout à fait compréhensible que ce soit une indication de la durée en millisecondes. Ce dernier est probablement un bout pour «pour toujours ou presque pour toujours».
Il n'y avait presque aucune chance de comprendre ce que font la plupart des registres ici, car ils sont utilisés par le code presque exclusivement en mode veille. Certains étaient dans
SFR
et certains étaient dans l'espace
MMIO
... J'ai pu copier le code et le reproduire. En particulier, j'étais intéressé par le fait que la minuterie de sommeil peut fonctionner à deux vitesses: avec une fréquence de 32KHz et 1Hz. Il s'agit d'un minuteur 24 bits, avec lequel le sommeil le plus court possible dure environ 30 ms, et le plus long peut durer environ 194 jours! En savoir plus dans le SDK.
Radio
La radio nécessite généralement une configuration étendue,
SFR
elle est donc trop encombrée dans un espace dense . La plupart des 8051 équipés de radios sont utilisés pour résoudre ce problème
MMIO
. Les E / S mappées en mémoire dans le 8051 sont généralement simplement mappées à l'espace d'adressage
XRAM
. En regardant le code en diagonale, je me suis rendu compte que la radio de cette puce était en place
MMIO:df00 — MMIO:dfff
.
Chemin RX
Encore une fois, j'ai décidé de commencer par l'image OTA. Il est suffisamment petit pour simplifier l'analyse. Il est vite devenu clair que l'image OTA n'envoie aucun paquet radio, mais les reçoit uniquement (les accusés de réception sont automatiquement envoyés au niveau matériel, ce qui est typique pour la plupart des puces ZigBee). Mais c'est bien! Grâce à cela, il nous suffit d'analyser seulement la moitié du pilote, ce qui signifie que la tâche est deux fois plus simple que possible!
Quand j'ai commencé à chercher où le code OTA obtient les données, il semblait qu'il y avait une file d'attente de tampon. Qu'est-ce que c'est: C'est une file d'attente contenant des octets individuels, chacun étant un pointeur vers une liste de tampons. Le code qui semblait recevoir des paquets et traiter les paquets reçus a pris le tampon de la file d'attente, l'a traité, puis l'a mis dans une autre file d'attente. Un schéma très simple. Une file d'attente stocke des tampons remplis de données reçues, une autre file d'attente stocke des tampons vides prêts à recevoir de nouvelles données reçues. Suffisamment clair.
En regardant un peu autour, nous découvrons rapidement où les files d'attente sont accessibles d'une manière différente: en supprimant le tampon de la file "vide" et en mettant en file d'attente les pleins. C'est le gestionnaire de l'interruption n ° 5! Le gestionnaire d' interruption lui - même était assez simple, à condition que le bit a été mis
TCON2.2
, il a sauvé
0xC6
dans
MMIO:df48
, dequeued le tampon, copié octets en elle et le mettre dans une autre file d' attente. Mais d'où a-t-il copié les octets? Où avez-vous la longueur de la copie? Les deux ont été extraits du tampon
XRAM
dans lequel il n'a pas écrit! Je n'ai jamais pu percer ce mystère.
La recherche ne s'est pas arrêtée là. L'interruption 4 a joué un rôle clé, son gestionnaire s'est avéré encore plus simple. Il a testé le bit 5
MMIO:dfad
(je l'appellerai
RADIO_IRQ4_pending
et, s'il est défini, il appelle une procédure qui n'est appelée nulle part ailleurs. Cette procédure a lu , vérifié que sa valeur était inférieure ou égale à 128, lu , vérifié qu'avec une augmentation de un, elle deviendrait égale à la valeur précédente. Si l'une des conditions ci-dessus n'était pas remplie, elle était enregistrée dans , sinon la page de configuration 4 était sélectionnée, la première valeur lue était stockée dans une variable globale, ce qui indiquait en outre la longueur. Cette valeur, moins un persisté , et le pointeur vers le tampon à partir de laquelle les données copiées stockées dans la suite : . Ensuite, le bit 2 a été installé .
SFR
FA
MMIO:df98
0xC6
MMIO:df48
D5
D4
D3
TRIGGER
Ici encore, la connaissance du contexte aide. 127 est la valeur maximale qu'un paquet 802.15.4 valide peut avoir, et cette longueur inclut un contrôle de redondance cyclique (CRC) de 2 octets, mais n'inclut pas la longueur de l'octet lui-même. Par conséquent, je suppose que
FA
c'est la longueur résultante (en tenant compte de la longueur d'octet et du CRC). Je l'ai nommé
RADIO_GOTLEN
. Dans un tel cas, il est logique que le
MMIO:df48
(maintenant nommé
RADIO_rxFirstByte
) puisse être le premier octet reçu (octet de longueur). Avec tous les registres restants, c'est clair:
D5
c'est la longueur du DMA pour RX DMA (maintenant appelé
RADIO_RXLEN
)
D4
:
D3
il est désassemblé en parties pointeur vers le RX DMA de destination (
RADIO_RXPTRH
et
RADIO_RXPTRL
respectivement).
Ensuite, tout a fonctionné. L'interruption numéro 4 est déclenchée dès que la radio reçoit un paquet dans la mémoire tampon interne. Le bit 5 réglé sur
RADIO_IRQ4_pending
(ceci est maintenant appelé
RADIO_IRQ4_pending
) nous indique que cela s'est produit. Nous procédons à l'inspection initiale du paquet (en nous assurant que sa longueur est dans des limites raisonnables), puis nous exécutons le DMA du tampon interne vers
XRAM
, si tout va bien. Sinon, nous écrivons
0xC6
dans
MMIO:df48
. Logiquement, cela peut être comparé à "vider le RX FIFO", d'où ce registre est maintenant appelé
RADIO_command
. Si tout allait bien avec le paquet et l'opération DMA terminée, alors le bit 2 est défini dans
TCON2
, et l'interruption 5. Ici encore, nous écrivons "vidage RX FIFO"
RADIO_command
. Ceci est utile car nous avons déjà extrait les données en utilisant la méthode DMA. Ensuite, les données sont copiées et le travail est terminé!
Dans la plupart des radios, le code de redondance cyclique reçu n'est pas fourni aux couches supérieures - il est simplement vérifié et renvoyé avec un seul bit d'état avec une valeur oui ou non. Comme d'habitude, il est conseillé de supposer que tout fonctionne "normalement". Vous vérifiez - c'est vraiment régulier. La plupart des radios ZigBee rapportent plutôt dans ces deux octets un LQI (indicateur de qualité de liaison radio) et RSSI (indicateur de force du signal reçu) plutôt qu'un CRC. Dans ce modèle, la radio fonctionne à peu près de la même manière. Presque. Apparemment, le premier octet est toujours
0xD0
mais le second semble contenir en fait le LQI (dans les 7 bits les moins significatifs) et l'état CRC (dans le bit 7). En fait, il est fonctionnellement très similaire au fonctionnement de la radio Chipcon. La commande
0xC6
signifie également "vide RX FIFO" pour les radios chipcon (maintenant TI)! Beaucoup d'autres choses ne sont pas les mêmes, mais les commandes sont OPPOSÉES , et cela m'a aidé à naviguer dans les autres éléments de cette pile radio!
En savoir plus sur la radio
Si vous considérez comment le code OTA lance la radio, vous pouvez voir que BEAUCOUP de registres ne sont affectés qu'une seule fois, certaines valeurs y sont écrites, ce qui semble complètement aléatoire. Très probablement, beaucoup d'entre eux sont de jauge. Tout registre qui est écrit une fois (ou à plusieurs reprises, mais la même valeur est entrée) est un registre d'étalonnage. Je vais sauter les détails ennuyeux des registres impliqués, mais je parlerai du code d'initiation fonctionnel qui se trouve dans le SDK.
Ici encore, nous observons combien de valeurs sont écrites dans le registre
RADIO_command
... Les valeurs enregistrées correspondent à celles que nous nous attendrions à voir si nous travaillions avec les valeurs des commandes chipcon, bien que nous puissions voir certaines valeurs qui ne sont pas dans les modules radio chipcon. Donc, soit cette radio est un bâtard de chipcon rare, soit ils descendent tous les deux d'un ancêtre commun. Dans tous les cas, cette situation aide à comprendre d'autres commandes émises par eux.
Reproduire le code d'initiation et écrire des gestionnaires d'interruptions, comme ceux intégrés à la puce, nous donne un binaire fonctionnel qui peut fonctionner pour la réception et est propice aux expériences. En remarquant d'autres registres sur lesquels le micrologiciel principal écrit, j'ai rapidement déterminé qu'il
MMIO:df88 — MMIO:df8f
s'agissait de "ma longue adresse MAC", qui sera utilisée au niveau matériel pour filtrer les paquets entrants. De même,
MMIO:df90 — MMIO:df91
définit le "propre PAN ID" pour le filtre RX. A
MMIO:df92 — MMIO:df93
définit "sa propre adresse courte". Cet équipement acceptera et accusera réception (ACK) de tout paquet envoyé à nos adresses de diffusion.
MMIO:dfc0
règle le canal radio selon la numérotation standard 802.15.4 (11..26).
Étant donné que la radio accusera réception des paquets, j'ai également pu constater que la
MMIO:dfc9
force de transmission était ajustée lors du réglage . Je pense qu'il s'agit du registre définissant la puissance TX. J'ai également remarqué que lorsqu'un canal est défini dans le micrologiciel principal de l'usine, deux autres registres sont écrits avec des valeurs par canal. Il n'y a qu'un seul de ces registres dans le micrologiciel OTA. Celui lié à RX est appelé
MMIO:dfcb
, et celui lié à TX est appelé
MMIO:dffd
... Assez facile à reproduire et à comprendre. Ensuite, il est temps de comprendre TX!
Envoyons quelques octets!
Après avoir déchiffré le chemin des données, j'ai transféré les noms de fonction et d'enregistrement dans mon image principale démontée. En regardant ce qui n'est toujours pas marqué, nous pouvons voir où se trouve le chemin de TX. En effet, il y a deux autres files d'attente de tampons ici: une pleine de tampons TX vides prêts à l'emploi, et l'autre pleine de tampons TX «épuisés» prêts à être envoyés. J'ai trouvé la fonction de transfert très rapidement.
En 802.15.4, il est habituel d'écouter le canal radio avant de transmettre. Cette opération est appelée CCA (Channel Idle Assessment). Avant de faire quoi que ce soit avec les données que nous sommes sur le point d'envoyer, considérons une boucle qui lit
MMIO:df98
et vérification du bit 0. S'il est défini, la fonction échoue et le temporisateur est réglé pour réessayer. Je pense que c'est la voie du CCA. Si nous voyons zéro dans ce bit 128 fois, alors nous considérons que le canal est libre.
La fonction de transfert elle-même s'est avérée extrêmement simple: vous sélectionnez la page de configuration 4, la longueur souhaitée (sans compter l'octet de longueur ou le CRC), et tout est écrit
CD
. Dans un pointeur vers un tampon
XRAM
écrit dans
CA
:
C9
. Le tampon commence par un octet de longueur.
RADIO_command
chargé de valeur
0xCB
. Il n'y a pas de telle commande dans les radios chipcon, mais je suppose que cela signifie "charger TX FIFO". Ensuite, le bit 1 est défini dans
TRIGGER
... Je suppose que c'est ainsi que l'accès DMA à la file d'attente radio interne TX FIFO est démarré. Puis
MMIO:dfc8
mis à
0xFF
, 255 tentatives sont faites pour attendre la fin de TX, en vérifiant que le bit 7
MMIO:df9b
(maintenant appelé
RADIO_curRfState
) est mis à 1. Ensuite, après un court délai, il est
MMIO:dfc8
réglé sur
0x7F
. Curieusement, je ne sais pas pourquoi il est enregistré
MMIO:dfc8
. Dans mon code, j'ai essayé de m'en passer et tout a bien fonctionné.
Queues
Après avoir expérimenté un peu, j'ai découvert quelques astuces que le firmware d'usine ne peut pas faire. Le bit 6 in
RADIO_IRQ4_pending
est mis à 1 après que nous "émettons" le paquet et que le délai d'accusé de réception (ACK) expire. Si nous recevons réellement un ACK, alors le bit 4. sera également mis à 1. Par conséquent, il est facile de déterminer (1) quand nous avons réellement envoyé un paquet et (2) si nous avons reçu un ACK. Frais!
De plus, si le bit 4 in
RADIO_IRQ4_pending
est activé et le bit 5 in n'est
RADIO_curRfState
pas occupé, cela signifie que nous sommes en train de recevoir un paquet. Nous devons sélectionner le RSSI manuellement, pour lequel nous lisons
MMIO:df84
(maintenant
RADIO_currentRSSI
). Il a un décalage d'environ 56 dBm.
J'ai également remarqué que le bit 1 dans
TCON2
réglé à la fin de TX DMA (mais pas nécessairement le processus TX lui-même). Le bit 0 in est
TCON2
mis à 1 lorsque l'initialisation de la radio se termine.
Mystères non résolus
Mesure ADC / batterie et moteur de chiffrement AES
Il est logique qu'il y ait un moyen de mesurer la tension de la batterie, mais je n'ai trouvé aucune trace d'un code similaire. Sans code qui utilise l'ADC de cette manière, les chances de trouver cette méthode de disparition sont minces. Le bloc AES est, en principe, le même que l'ADC. Je sais qu'il y a un bloc d'accélération AES dans la puce (requis pour ZigBee). Mais comme le code réel ne l'utilise pas, je ne vois pas de moyen de le trouver.
miscellanea
Des choses que nous ne pouvons pas trouver, mais dont nous ne nous soucions pas vraiment, car nous ne pouvons pas acheter cette puce: contrôleur LED IR, unité PWM, DAC. Je laisserai ces choses au lecteur pour qu'il s'exerce seul.
Brochage ZBS242 / 3, fonctionnalités, SFR, téléchargements
Téléchargez le SDK ZBS24x .
- Les cellules grisées indiquent les registres adressables au niveau du bit
- Les registres ombrés en diagonale qui ne sont pas stockés à la banque
CFGPAGE
- Registres ombrés verticaux qui, apparemment, n'apparaissent sur aucune des pages.
- Les cellules vides sont des registres inconnus
- Les noms des registres RADIO commencent par la lettre "r"
Leçons pour un ingénieur inverse débutant
- Lisez le matériel pendant au moins quelques heures ou quelques jours avant de commencer le travail.
- - . -, .
- . SPI , I2C , . .
- , – ( ).
- : , . , , .
- , , .
- . - , - . , .
- - . , , .
- , , . , , .
- - . .
- - , , - .
Les serveurs cloud de Macleod sont parfaits pour l'hébergement de sites Web.
Inscrivez-vous en utilisant le lien ci-dessus ou en cliquant sur la bannière et bénéficiez d'une remise de 10% pour le premier mois de location d'un serveur de toute configuration!