Modbus no microcontrolador russo K1986BE92QI

Caí nas mãos do microcontrolador russo K1986BE92QI fabricado pela PKK Milander JSC com núcleo RISC de 32 bits ARM Cortex-M3 128kB Flash e 32kB RAM, imediatamente queria estudá-lo e testá-lo em ação.


O microcontrolador vem em um pacote que os chineses invejam com o AliExpress. O chip está em um cassete de papel alumínio grosso, embrulhado em papel alumínio, revestido com espuma de borracha, e todo esse “sanduíche” em uma caixa de papelão com paredes internas cobertas com papel alumínio. Em geral, proteção contra eletricidade estática em altitude.




Para o microcontrolador há uma etiqueta e um protocolo de seleção de produtos, o que é muito bom.



Para começar, foi necessário desenvolver um diagrama de circuitos da placa de depuração e determinar os componentes. Ele parou no mínimo de componentes: um estabilizador de 3,3v para alimentação de uma porta USB, um ressonador de quartzo de 8MHz, um conector miniUSB, um botão de reinicialização, resistores pull-up e conectores SIP. Eu acho que os experimentos iniciais com o microcontrolador são suficientes. Defina também a opção smd para selecionar o modo do carregador de inicialização embutido. O microcontrolador permite escolher o método de baixar o programa usando uma das duas interfaces seriais UART ou JTAG / SWD, enquanto o JTAG permite depurar o programa no microcontrolador. A escolha do método de carregamento do programa é determinada pelos níveis lógicos nas saídas PF4, PF5, PF6. Todas as opções possíveis são apresentadas na tabela:



O microcontrolador é um chip fabricado em uma caixa plástica LQFP64 com pinos de 0,3 mm de largura e 0,2 mm entre eles, o que sugeria a impossibilidade de criar uma placa de circuito impresso de qualidade aceitável usando a tecnologia LUT, mas a experiência confirmou o contrário. Em algumas horas, um desenho de PCB foi feito no Sprint Layout, impresso em papel de alta densidade Lamond e transferido para fibra de vidro. A gravação ocorreu em uma solução de peróxido e ácido cítrico a olho e levou cerca de uma hora; surpreendentemente, a qualidade dos condutores foi aceitável na primeira vez, o que agradou.



E assim que a placa é criada, todos os componentes são soldados, resta programar. Usaremos o ambiente de desenvolvimento do Keil - MDK ARM uVision 5.0, o pacote de software Standard Peripherals Library + é distribuído a ele pelo fabricante do microcontrolador. Como não queria programar no UART, decidi usar o programador / depurador de circuito ST-Link v2, ou melhor, seu clone de um fabricante chinês desconhecido. O Keil suporta-o imediatamente, mas o microcontrolador, embora a documentação diga que suporta a interface SWD, esqueceu como e onde conectá-lo. Depois de pesquisar na Internet: “JTAG - SWD adapter”, verificou-se que a linha SWDIO se conecta ao JTAG-TMS, e SWCLK ao JTAG-TCK e “Oh, milagre!” tudo funcionou, um programa de teste foi inserido no microcontrolador.



Este foi o fim da alegria, porque após o firmware, o microcontrolador, embora funcionasse, parece não ser mais um depurador. Aparentemente, depois de piscar, as linhas da porta JTAG-A são redefinidas para outra finalidade funcional, embora na porta do programa B na qual o JTAG-A está localizado nem tenha sido inicializada. Eu não queria entender isso, já que também existe um JTAG-B. Quando conectado a uma interface JTAG alternativa, tudo funcionava como um relógio. No futuro, vamos usá-lo para programação e depuração.


A primeira tarefa definida para mim foi conectar o controlador ao sistema SCADA usando o protocolo Modbus. Para não reinventar a roda, pegue a biblioteca freemodbus freemodbus de plataforma cruzada e leve -a para o nosso microcontrolador.


Para criar projetos no Keil em um microcontrolador da Milander, você primeiro precisa instalar o pacote de software. Isso é feito com um simples clique duplo no arquivo. Em seguida, Keil fará tudo sozinha.


E assim criamos um novo projeto. Selecionamos nossos componentes de microcontrolador e biblioteca que precisamos:



Na árvore do projeto, crie o grupo Modbus Slave e adicione os seguintes arquivos da biblioteca Freemodbus:



E não esqueça nas opções do projeto para indicar ao compilador os seguintes caminhos para os diretórios da biblioteca.



Agora você pode começar a portar especificamente a biblioteca Freemodbus para o nosso microcontrolador usando a Biblioteca de Periféricos Padrão. Para fazer isso, você precisa especificar a função de inicialização da porta UART xMBPortSerialInit no arquivo 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; } 

função de gravação e leitura:


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

Manipulador de interrupção 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( ); } } 

Depois disso, editamos o arquivo portimer.c no qual está configurado um timer que gera relatórios temporários para rastrear o final do pacote do 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( ); } 

Em main.c, adicionaremos funções de processamento de registro modbus, abafaremos registros não utilizados com stubs


 /* ----------------------- 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 é o fim da portabilidade, permanece apenas na função int main (void) para inicializar a biblioteca e chamar eMBPoll () em um loop;


 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 tudo sem erros, mas nada funciona. No modo de depuração, descobrimos que os pacotes estão sendo processados ​​e o programa trava na inicialização da transmissão. Quando uma interrupção é ativada pelo transmissor UART, a interrupção não é chamada e o programa entra em um loop infinito. Depois de estudar a seção "Descrição do UART", as especificações no microcontrolador se depararam com uma nota:


A interrupção do transmissor funciona na borda, não no nível do sinal. Se o módulo e as interrupções forem permitidos antes que os dados sejam gravados no buffer FIFO do transmissor, uma interrupção não será gerada. Uma interrupção ocorre apenas quando o buffer FIFO está vazio.

Bem, não importa, estamos procurando onde a transferência começa. No arquivo mbrtu.c, encontramos as linhas de código


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

e forçosamente enviar o byte para o transmissor UART, para isso adicionamos a linha: "xMBRTUTransmitFSM ();" e tudo começa a funcionar bem, os pacotes são executados, os registros são lidos e, em seguida, é uma questão de tecnologia.

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


All Articles