Ingénierie inverse d'un microcontrôleur inconnu





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  sonne0xA0



 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!






All Articles