Caí en manos del microcontrolador ruso K1986BE92QI fabricado por PKK Milander JSC con núcleo RISC de 32 bits ARM Cortex-M3 128kB Flash y 32kB RAM, inmediatamente quise estudiarlo y probarlo en acción.
El microcontrolador viene en un paquete que los chinos envidian con AliExpress. El chip se encuentra en un cassette de papel de aluminio grueso, que está envuelto en papel de aluminio, colocado con goma espuma, y todo este "sándwich" en una caja de cartón con paredes interiores cubiertas con papel de aluminio. En general, protección contra la electricidad estática en altitud.


Para el microcontrolador hay una etiqueta y un protocolo de selección de productos, lo cual es muy bueno.

Para empezar, era necesario desarrollar un diagrama de circuito de la placa de depuración y determinar los componentes. Se detuvo en un mínimo de componentes: un estabilizador de 3.3v para la alimentación de un puerto USB, un resonador de cuarzo de 8MHz, un conector miniUSB, un botón de reinicio, resistencias pull-up y conectores sorbo. Creo que para los experimentos iniciales con el microcontrolador es suficiente. También configure el interruptor smd para seleccionar el modo del gestor de arranque incorporado. El microcontrolador le permite elegir el método de descarga del programa utilizando una de las dos interfaces seriales UART o JTAG / SWD, mientras que JTAG le permite depurar el programa en el microcontrolador. La elección del método de carga del programa está determinada por los niveles lógicos en las salidas PF4, PF5, PF6. Todas las opciones posibles se presentan en la tabla:

El microcontrolador es un chip hecho en una caja de plástico LQFP64 con pines de 0.3 mm de ancho y 0.2 mm entre ellos, lo que sugiere la imposibilidad de crear una placa de circuito impreso de calidad aceptable utilizando tecnología LUT, pero la experiencia ha confirmado lo contrario. En un par de horas, se realizó un dibujo de PCB en Sprint Layout, impreso en papel Lamond de alta densidad y transferido a fibra de vidrio. El grabado se realizó en una solución de peróxido y ácido cítrico por ojo y tomó aproximadamente una hora, sorprendentemente, la calidad de los conductores fue aceptable la primera vez, lo que agradó.

Y así se crea la placa, se sueldan todos los componentes, queda por programar. Utilizaremos el entorno de desarrollo de Keil - MDK ARM uVision 5.0, el paquete de software Standard Peripherals Library + se lo distribuye el fabricante del microcontrolador. No quería programar en UART, así que decidí usar el programador / depurador en circuito ST-Link v2, o más bien, su clon de un fabricante chino desconocido. Keil lo admite de fábrica, pero el microcontrolador, aunque la documentación dice que admite la interfaz SWD, olvidó cómo y dónde conectarlo. Después de buscar en Internet: "JTAG - Adaptador SWD", se descubrió que la línea SWDIO se conecta a JTAG-TMS, y SWCLK a JTAG-TCK y "¡Oh milagro!" todo funcionó, se introdujo un programa de prueba en el microcontrolador.

Este fue el final de la alegría, porque después del firmware, el microcontrolador, aunque funcionó, parece haber dejado de funcionar con el depurador. Aparentemente, después de parpadear, las líneas de puerto JTAG-A se redefinen para otro propósito funcional, aunque en el puerto B del programa en el que se encuentra JTAG-A ni siquiera se inicializó. No quería entender esto, ya que también hay un JTAG-B. Cuando se conecta a una interfaz JTAG alternativa, todo funcionaba como un reloj. En el futuro, lo utilizaremos para programar y depurar.
La primera tarea establecida para mí fue conectar el controlador al sistema SCADA utilizando el protocolo Modbus. Para no reinventar la rueda, tomamos la biblioteca multiplataforma freemodbus freemodbus y la portamos para nuestro microcontrolador.
Para crear proyectos en Keil en un microcontrolador de Milander, primero debe instalar el paquete de software. Esto se hace con un simple doble clic en el archivo. Luego Keil hará todo por sí misma.
Y así creamos un nuevo proyecto. Seleccionamos nuestros componentes de microcontrolador y biblioteca que necesitamos:

En el árbol del proyecto, cree el grupo Modbus Slave y agregue los siguientes archivos de la biblioteca Freemodbus:

Y no olvide en las opciones del proyecto indicarle al compilador las siguientes rutas a los directorios de la biblioteca.

Ahora puede comenzar a portar específicamente la biblioteca Freemodbus a nuestro microcontrolador utilizando la Biblioteca de periféricos estándar. Para hacer esto, debe especificar la función de inicialización del puerto UART xMBPortSerialInit en el archivo 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; }
función de escritura y lectura:
BOOL xMBPortSerialPutByte( CHAR ucByte ) { // UART_SendData(MDR_UART2,ucByte); return TRUE; } BOOL xMBPortSerialGetByte( CHAR * pucByte ) { // *pucByte = (uint8_t) UART_ReceiveData(MDR_UART2); return TRUE; }
Manejador de interrupciones 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( ); } }
Después de esto, editamos el archivo portimer.c en el que se configura un temporizador que genera informes temporales para rastrear el final del paquete de protocolo 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( ); }
En main.c agregaremos funciones de procesamiento de registros modbus, amortiguaremos los registros no utilizados con apéndices
/* ----------------------- 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; }
Este es el final de la transferencia, solo permanece en la función int main (void) para inicializar la biblioteca y llamar a eMBPoll () en un bucle;
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]++; } }
Compilamos todo sin errores, pero nada funciona. En modo de depuración, descubrimos que los paquetes se están procesando y el programa se cuelga en la inicialización de la transmisión. Cuando se activa una interrupción desde el transmisor UART, no se llama a la interrupción y el programa entra en un bucle infinito. Después de estudiar la sección "Descripción de las especificaciones UART" en el microcontrolador, apareció una Nota:
La interrupción del transmisor funciona en el borde, no en el nivel de la señal. Si el módulo y sus interrupciones están permitidos antes de que los datos se escriban en el búfer FIFO del transmisor, no se genera una interrupción. Solo se produce una interrupción cuando el búfer FIFO está vacío.
Bueno, no importa, estamos buscando dónde comienza la transferencia, en el archivo mbrtu.c encontramos las líneas de código
/* Activate the transmitter. */ eSndState = STATE_TX_XMIT; vMBPortSerialEnable( FALSE, TRUE );
y enviamos por la fuerza el byte al transmisor UART, para esto agregamos la línea: "xMBRTUTransmitFSM ();" y todo comienza a funcionar bien, los paquetes se ejecutan, los registros se leen y luego es cuestión de tecnología.