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.