Modbus auf dem russischen Mikrocontroller K1986BE92QI

Ich bin in die Hände des russischen Mikrocontrollers K1986BE92QI gefallen , der von PKK Milander JSC mit 32-Bit-RISC-Kern ARM Cortex-M3 128 kB Flash und 32 kB RAM hergestellt wurde. Ich wollte ihn sofort in Aktion studieren und testen.


Der Mikrocontroller wird in einem Paket geliefert, um das die Chinesen mit AliExpress beneiden. Der Chip liegt in einer Kassette aus dicker Aluminiumfolie, die in Folienpapier eingewickelt ist, mit Schaumgummi ausgelegt ist, und dieses ganze „Sandwich“ in einem Karton mit mit Folie bedeckten Innenwänden. Im Allgemeinen Schutz vor statischer Elektrizität in der Höhe.




Zum Mikrocontroller gehört ein Etikett und ein Produktauswahlprotokoll, was sehr schön ist.



Zunächst war es notwendig, einen Schaltplan der Debug-Karte zu entwickeln und die Komponenten zu bestimmen. Er stoppte bei einem Minimum an Komponenten: einem 3,3-V-Stabilisator für die Stromversorgung über einen USB-Anschluss, einem 8-MHz-Quarzresonator, einem Mini-USB-Anschluss, einem Reset-Knopf, Pull-up-Widerständen und Schluck-Anschlüssen. Ich denke für die ersten Experimente mit dem Mikrocontroller ist genug. Stellen Sie auch den smd-Schalter ein, um den Modus des eingebauten Bootloaders auszuwählen. Mit dem Mikrocontroller können Sie die Methode zum Herunterladen des Programms über eine von zwei seriellen Schnittstellen UART oder JTAG / SWD auswählen, während Sie mit JTAG das Programm im Mikrocontroller debuggen können. Die Wahl der Programmlademethode wird durch die Logikpegel an den Ausgängen PF4, PF5, PF6 bestimmt. Alle möglichen Optionen sind in der Tabelle dargestellt:



Der Mikrocontroller ist ein Chip aus einem Kunststoffgehäuse LQFP64 mit 0,3 mm breiten und 0,2 mm dazwischen liegenden Stiften, was darauf hindeutet, dass mit der LUT-Technologie keine Leiterplatte von akzeptabler Qualität hergestellt werden kann. Die Erfahrung hat jedoch das Gegenteil bestätigt. In wenigen Stunden wurde eine PCB-Zeichnung im Sprint-Layout erstellt, auf Lamond-Papier mit hoher Dichte gedruckt und auf Glasfaser übertragen. Das Ätzen erfolgte in einer Lösung von Peroxid und Zitronensäure mit dem Auge und dauerte etwa eine Stunde, überraschenderweise war die Qualität der Leiter beim ersten Mal akzeptabel, was erfreute.



Und so wird die Platine erstellt, alle Komponenten werden verlötet, es bleibt zu programmieren. Wir werden die Entwicklungsumgebung von Keil - MDK ARM uVision 5.0 verwenden, das Softwarepaket Standard Peripherals Library + wird vom Hersteller des Mikrocontrollers an Keil verteilt. Ich wollte nicht in UART programmieren, deshalb habe ich mich für den ST-Link v2 In-Circuit-Programmierer / Debugger oder dessen Klon eines unbekannten chinesischen Herstellers entschieden. Keil unterstützt es sofort, aber der Mikrocontroller hat vergessen, wie und wo er angeschlossen werden soll, obwohl in der Dokumentation angegeben ist, dass er die SWD-Schnittstelle unterstützt. Nach der Suche im Internet nach: "JTAG - SWD-Adapter" wurde festgestellt, dass die SWDIO-Leitung eine Verbindung zu JTAG-TMS und SWCLK zu JTAG-TCK und "Oh Wunder!" Alles hat funktioniert, ein Testprogramm wurde in den Mikrocontroller geflasht.



Dies war das Ende der Freude, denn nach der Firmware scheint der Mikrocontroller, obwohl er funktioniert hat, nicht mehr mit dem Debugger zu arbeiten. Anscheinend werden nach dem Flashen die JTAG-A-Portleitungen für einen anderen funktionalen Zweck neu definiert, obwohl in dem Programmport B, auf dem sich JTAG-A befindet, nicht einmal initialisiert wurde. Ich wollte das nicht verstehen, da es auch einen JTAG-B gibt. Bei Anschluss an eine alternative JTAG-Schnittstelle funktionierte alles wie eine Uhr. In Zukunft werden wir es zum Programmieren und Debuggen verwenden.


Die erste Aufgabe für mich bestand darin, den Controller über das Modbus-Protokoll mit dem SCADA-System zu verbinden. Um das Rad nicht neu zu erfinden, nehmen wir die plattformübergreifende Freemodbus-Freemodbus- Bibliothek und portieren sie für unseren Mikrocontroller.


Um Projekte in Keil auf einem Mikrocontroller von Milander zu erstellen, müssen Sie zuerst das Softwarepaket installieren. Dies erfolgt durch einen einfachen Doppelklick auf die Datei. Als nächstes wird Keil alles selbst machen.


Und so erstellen wir ein neues Projekt. Wir wählen unsere Mikrocontroller- und Bibliothekskomponenten aus, die wir benötigen:



Erstellen Sie im Projektbaum die Modbus-Slave-Gruppe und fügen Sie dort die folgenden Dateien aus der Freemodbus-Bibliothek hinzu:



Vergessen Sie in den Projektoptionen nicht, dem Compiler die folgenden Pfade zu den Bibliotheksverzeichnissen anzugeben.



Jetzt können Sie beginnen, die Freemodbus-Bibliothek mithilfe der Standard-Peripheriebibliothek gezielt auf unseren Mikrocontroller zu portieren. Dazu müssen Sie die Initialisierungsfunktion des UART-Ports xMBPortSerialInit in der Datei portserial.c angeben


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; } 

Schreib- und Lesefunktion:


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

UART Interrupt Handler


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

Anschließend bearbeiten wir die Datei portimer.c, in der ein Timer konfiguriert ist, der temporäre Berichte generiert, um das Ende des Modbus-Protokollpakets zu verfolgen.


 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( ); } 

In main.c werden wir Modbus-Registerverarbeitungsfunktionen hinzufügen und nicht verwendete Register mit Stubs dämpfen


 /* ----------------------- 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; } 

Dies ist das Ende der Portierung. Es verbleibt nur in der Funktion int main (void), um die Bibliothek zu initialisieren und eMBPoll () in einer Schleife aufzurufen.


 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]++; } } 

Wir kompilieren alles fehlerfrei, aber nichts funktioniert. Im Debug-Modus stellen wir fest, dass Pakete verarbeitet werden und das Programm an der Übertragungsinitialisierung hängt. Wenn ein Interrupt vom UART-Sender eingeschaltet wird, wird der Interrupt nicht aufgerufen und das Programm geht in eine Endlosschleife. Nach dem Studium des Abschnitts „Beschreibung des UART“ wurden die Spezifikationen des Mikrocontrollers auf einen Hinweis gestoßen:


Die Senderunterbrechung funktioniert an der Flanke, nicht auf dem Signalpegel. Wenn das Modul und seine Interrupts zulässig sind, bevor die Daten in den Sender-FIFO-Puffer geschrieben werden, wird kein Interrupt generiert. Ein Interrupt tritt nur auf, wenn der FIFO-Puffer leer ist.

Nun, es spielt keine Rolle, wir suchen, wo die Übertragung beginnt. In der Datei mbrtu.c finden wir die Codezeilen


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

und senden Sie das Byte zwangsweise an den UART-Sender. Dazu fügen wir die Zeile hinzu: "xMBRTUTransmitFSM ();" und alles beginnt gut zu funktionieren, Pakete laufen, Register werden gelesen und dann ist es eine Frage der Technologie.

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


All Articles