Modbus sur le microcontrôleur russe K1986BE92QI

Je suis tombé entre les mains du microcontrôleur russe K1986BE92QI fabriqué par PKK Milander JSC avec un coeur RISC 32 bits ARM Cortex-M3 128kB Flash et 32kB RAM, j'ai immédiatement voulu l'étudier et le tester en action.


Le microcontrôleur est livré dans un emballage que les Chinois envient avec AliExpress. La puce se trouve dans une cassette de papier d'aluminium épais, qui est enveloppé dans du papier d'aluminium, recouvert de caoutchouc mousse, et tout ce "sandwich" dans une boîte en carton avec des parois intérieures recouvertes de papier d'aluminium. En général, protection contre l'électricité statique en altitude.




Pour le microcontrôleur, il y a une étiquette et un protocole de sélection de produits, ce qui est très agréable.



Pour commencer, il était nécessaire de développer un schéma de circuit de la carte de débogage et de déterminer les composants. Il s'est arrêté sur un minimum de composants: un stabilisateur 3,3 V pour l'alimentation à partir d'un port USB, un résonateur à quartz 8 MHz, un connecteur miniUSB, un bouton de réinitialisation, des résistances de rappel et des connecteurs SIP. Je pense que pour les premières expériences avec le microcontrôleur, c'est suffisant. Réglez également le commutateur smd pour sélectionner le mode du chargeur de démarrage intégré. Le microcontrôleur vous permet de choisir la méthode de téléchargement du programme à l'aide de l'une des deux interfaces série UART ou JTAG / SWD, tandis que JTAG vous permet de déboguer le programme dans le microcontrôleur. Le choix de la méthode de chargement du programme est déterminé par les niveaux logiques aux sorties PF4, PF5, PF6. Toutes les options possibles sont présentées dans le tableau:



Le microcontrôleur est une puce fabriquée dans un boîtier en plastique LQFP64 avec des broches de 0,3 mm de large et 0,2 mm entre elles, ce qui suggère l'impossibilité de créer une carte de circuit imprimé de qualité acceptable en utilisant la technologie LUT, mais l'expérience a confirmé le contraire. En quelques heures, un dessin PCB a été réalisé en Sprint Layout, imprimé sur du papier Lamond haute densité et transféré sur fibre de verre. La gravure a eu lieu dans une solution de peroxyde et d'acide citrique à l'œil nu et a pris environ une heure, ce qui est surprenant, la qualité des conducteurs était acceptable la première fois, ce qui a plu.



Et donc la carte est créée, tous les composants sont soudés, il reste à programmer. Nous utiliserons l'environnement de développement de Keil - MDK ARM uVision 5.0, le pack logiciel Standard Peripherals Library + lui est distribué par le fabricant du microcontrôleur. Je ne voulais pas programmer en UART, j'ai donc décidé d'utiliser le programmeur / débogueur en circuit ST-Link v2, ou plutôt son clone d'un fabricant chinois inconnu. Keil le prend en charge immédiatement, mais le microcontrôleur, bien que la documentation indique qu'il prend en charge l'interface SWD, a oublié comment et où le connecter. Après avoir recherché sur Internet: «JTAG - adaptateur SWD», il a été constaté que la ligne SWDIO se connecte à JTAG-TMS, et SWCLK à JTAG-TCK et «Oh miracle!» tout a fonctionné, un programme de test a été flashé dans le microcontrôleur.



Ce fut la fin de la joie, car après le firmware, le microcontrôleur, bien qu'il ait fonctionné, semble avoir cessé de fonctionner avec le débogueur. Apparemment, après avoir clignoté, les lignes de port JTAG-A sont redéfinies dans un autre but fonctionnel, bien que dans le programme, le port B sur lequel se trouve JTAG-A n'ait même pas été initialisé. Je ne voulais pas comprendre cela, car il y a aussi un JTAG-B. Lorsqu'il est connecté à une autre interface JTAG, tout fonctionne comme une horloge. À l'avenir, nous l'utiliserons pour la programmation et le débogage.


La première tâche pour moi a été de connecter le contrôleur au système SCADA en utilisant le protocole Modbus. Afin de ne pas réinventer la roue, prenez la librairie multiplateforme freemodbus freemodbus et portez-la sur notre microcontrôleur.


Pour créer des projets dans Keil sur un microcontrôleur de Milander, vous devez d'abord installer le pack logiciel. Cela se fait par un simple double clic sur le fichier. Ensuite, Keil fera tout elle-même.


Et donc nous créons un nouveau projet. Nous sélectionnons nos microcontrôleurs et composants de bibliothèque dont nous avons besoin:



Dans l'arborescence du projet, créez le groupe Modbus Slave et ajoutez-y les fichiers suivants à partir de la bibliothèque Freemodbus:



Et n'oubliez pas dans les options du projet d'indiquer au compilateur les chemins suivants vers les répertoires de la bibliothèque.



Vous pouvez maintenant commencer à porter spécifiquement la bibliothèque Freemodbus sur notre microcontrôleur à l'aide de la bibliothèque de périphériques standard. Pour ce faire, vous devez spécifier la fonction d'initialisation du port UART xMBPortSerialInit dans le fichier portserial.c


BOOL xMBPortSerialInit( UCHAR ucPORT, ULONG ulBaudRate, UCHAR ucDataBits, eMBParity eParity ) { //    F RST_CLK_PCLKcmd(RST_CLK_PCLK_PORTF, ENABLE); //      PORT_InitTypeDef uart2_port_set; //   F   UART //     PORT_StructInit(&uart2_port_set); //    uart2_port_set.PORT_FUNC = PORT_FUNC_OVERRID; //    uart2_port_set.PORT_SPEED = PORT_SPEED_MAXFAST; //     uart2_port_set.PORT_MODE = PORT_MODE_DIGITAL; //   PF1  UART_TX () uart2_port_set.PORT_Pin = PORT_Pin_1; uart2_port_set.PORT_OE = PORT_OE_OUT; PORT_Init(MDR_PORTF, &uart2_port_set); //   PF0  UART_RX () uart2_port_set.PORT_Pin = PORT_Pin_0; uart2_port_set.PORT_OE = PORT_OE_IN; //    UART //   UART2 RST_CLK_PCLKcmd(RST_CLK_PCLK_UART2, ENABLE); //      UART UART_InitTypeDef UART_InitStructure; //    UART = 1 UART_BRGInit(MDR_UART2,UART_HCLKdiv1); //  UART //    – 115200  UART_InitStructure.UART_BaudRate = ulBaudRate; //     – 8 UART_InitStructure.UART_WordLength = UART_WordLength8b; //  - UART_InitStructure.UART_StopBits = UART_StopBits1; //    UART_InitStructure.UART_Parity = UART_Parity_No; //    FIFO   , // ..      UART_InitStructure.UART_FIFOMode = UART_FIFO_OFF; //      UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_RXE | UART_HardwareFlowControl_TXE; //  UART2    UART_Init(MDR_UART2, &UART_InitStructure); //   UART UART_Cmd(MDR_UART2, ENABLE); return TRUE; } 

fonction d'écriture et de lecture:


 BOOL xMBPortSerialPutByte( CHAR ucByte ) { //  UART_SendData(MDR_UART2,ucByte); return TRUE; } BOOL xMBPortSerialGetByte( CHAR * pucByte ) { //  *pucByte = (uint8_t) UART_ReceiveData(MDR_UART2); return TRUE; } 

Gestionnaire d'interruption UART


  void USART2_IRQHandler(void) { /*     ---------------------------------------------------*/ if((UART_GetITStatus(MDR_UART2,UART_IT_RX)) != RESET) { prvvUARTRxISR( ); } /*     ------------------------------------------------*/ if((UART_GetITStatus(MDR_UART2,UART_IT_TX)) !=RESET) { prvvUARTTxReadyISR( ); } } 

Ensuite, nous éditons le fichier portimer.c dans lequel un temporisateur est configuré qui génère des rapports temporaires pour suivre la fin du paquet de protocole Modbus.


 BOOL xMBPortTimersInit( USHORT usTim1Timerout50us ) { MDR_RST_CLK->PER_CLOCK |= (1<<14); //   TIM1 MDR_RST_CLK->TIM_CLOCK = 0x0; MDR_RST_CLK->TIM_CLOCK |= (1<<24); // TIM1_CLK_EN MDR_RST_CLK->TIM_CLOCK |= 0x07; // HCLK/8   MDR_TIMER1->CNTRL = 0x00000002; //   MDR_TIMER1->CNT = 0x00000000; //  MDR_TIMER1->PSG = 0x2; //f/1   while((MDR_TIMER1->CNTRL & 0x004) != 0) {__NOP();} //    MDR_TIMER1->ARR = usTim1Timerout50us; //     while((MDR_TIMER1->CNTRL & 0x004) != 0) {__NOP();} //     MDR_TIMER1->IE = 0x00000002; //(CNT==ARR)->IE      NVIC->ISER[0] = (1<<14); // Global EN for IRQ14   MDR_TIMER1->CNTRL |= (1<<0); //Timer1 ON   return TRUE; } inline void vMBPortTimersEnable( ) { /*    */ MDR_TIMER1->CNTRL |= (1<<0); //Timer1 ON } inline void vMBPortTimersDisable( ) { /*    */ MDR_TIMER1->CNTRL &= ~(1<<0); //Timer1 OFF } static void prvvTIMERExpiredISR( void ) { ( void )pxMBPortCBTimerExpired( ); } void Timer1_IRQHandler(void) { //   MDR_TIMER1->STATUS &= ~0x002; //IE FLAG=0 prvvTIMERExpiredISR( ); } 

Dans main.c, nous ajouterons des fonctions de traitement des registres modbus, nous assourdirons les registres inutilisés avec des talons


 /* ----------------------- Defines ------------------------------------------*/ #define REG_INPUT_START 1000 #define REG_INPUT_NREGS 4 /* ----------------------- Static variables ---------------------------------*/ static USHORT usRegInputStart = REG_INPUT_START; static USHORT usRegInputBuf[REG_INPUT_NREGS]; eMBErrorCode eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs ) { eMBErrorCode eStatus = MB_ENOERR; int iRegIndex; if( ( usAddress >= REG_INPUT_START ) && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) ) { iRegIndex = ( int )( usAddress - usRegInputStart ); while( usNRegs > 0 ) { *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 ); *pucRegBuffer++ = ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF ); iRegIndex++; usNRegs--; } } else { eStatus = MB_ENOREG; } return eStatus; } eMBErrorCode eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs, eMBRegisterMode eMode ) { return MB_ENOREG; } eMBErrorCode eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils, eMBRegisterMode eMode ) { return MB_ENOREG; } eMBErrorCode eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete ) { return MB_ENOREG; } 

C'est la fin du portage, il ne reste que dans la fonction int main (void) pour initialiser la bibliothèque et appeler eMBPoll () en boucle;


 int main (void) { eMBErrorCode eStatus; // UART    ,   portserial.c  xMBPortSerialInit    eStatus = eMBInit( MB_RTU, 0x0A, 0, 19200, MB_PAR_NONE ); /* Enable the Modbus Protocol Stack. */ eStatus = eMBEnable( ); while(1) { eStatus = eMBPoll( ); //  if (eStatus!= MB_ENOREG){}; /* Here we simply count the number of poll cycles. */ usRegInputBuf[0]++; } } 

Nous compilons tout sans erreurs, mais rien ne fonctionne. En mode débogage, nous découvrons que les paquets sont en cours de traitement et le programme se bloque sur l'initialisation de la transmission. Lorsqu'une interruption est activée à partir de l'émetteur UART, l'interruption n'est pas appelée et le programme entre dans une boucle infinie. Après avoir étudié la section «Description de l'UART», les spécifications du microcontrôleur sont tombées sur une note:


L'interruption de l'émetteur fonctionne sur le bord, pas sur le niveau du signal. Si le module et ses interruptions sont autorisés avant que les données ne soient écrites dans le tampon FIFO de l'émetteur, aucune interruption n'est générée. Une interruption se produit uniquement lorsque le tampon FIFO est vide.

Eh bien, peu importe, nous cherchons où commence le transfert, dans le fichier mbrtu.c nous trouvons les lignes de code


 /* Activate the transmitter. */ eSndState = STATE_TX_XMIT; vMBPortSerialEnable( FALSE, TRUE ); 

et envoyer de force l'octet à l'émetteur UART, pour cela nous ajoutons la ligne: "xMBRTUTransmitFSM ();" et tout commence à bien fonctionner, les paquets fonctionnent, les registres sont lus, puis c'est une question de technologie.

Source: https://habr.com/ru/post/fr438534/


All Articles