Хабрахабр

[Из песочницы] Modbus на российском микроконтроллере К1986ВЕ92QI

Попал мне в руки российский микроконтроллер К1986ВЕ92QI производства АО "ПКК Миландр" с 32-битным RISC ядром ARM Cortex-M3 128кБ Flash и 32кБ ОЗУ, сразу же захотелось изучить и опробовать его в действии.

Микросхема лежит в кассете из толстой алюминиевой фольги, которая обернута фольгированной бумагой, проложено поролоном, и весь этот "бутерброд" в картонной коробке с внутренними стенками покрытыми фольгой. Микроконтроллер поставляется в упаковке, которой позавидуют китайцы с AliExpress. Вообщем защита от статического электричества на высоте.

К микроконтроллеру идет этикетка и протокол отбора продукции, что очень даже приятно.

Остановился на минимуме компонентов: стабилизатор на 3. Для начала нужно было разработать принципиальную схему отладочной платы и определится с компонентами. Думаю для начальных опытов с микроконтроллером хватит. 3в для питания от USB порта, кварцевый резонатор на 8МГц, разъем miniUSB, кнопка сброса, подтягивающие резисторы и sip разъемы. Микроконтроллер позволяет выбирать метод загрузки программы по одному из двух последовательных интерфейсов UART или JTAG/SWD, при этом JTAG позволяет производить отладку программы в микроконтроллере. Также установил smd переключатель для выбора режима встроенного загрузчика. Все возможные варианты представлены в таблице: Выбор метода загрузки программы определяется логическими уровнями на выходах PF4, PF5, PF6.

3мм и с расстоянием между ними 0. Микроконтроллер представляет из себя микросхему выполненную в пластиковом корпусе LQFP64 с выводами шириной 0. За пару часов был сделан чертеж печатной платы в Sprint Layout, распечатан на бумаге повышенной плотности Lamond и перенесен на стеклотекстолит. 2мм, что навело на мысль о невозможности создать печатную плату приемлемого качества по технологии ЛУТ, но опыт подтвердил обратное. Травление происходило в растворе перекиси и лимонной кислоты на глазок и заняло около часа, на удивление качество проводников оказалось приемлемым с первого раза, что порадовало.

Будем использовать среду для разработки от Keil — MDK ARM uVision 5. И так плата создана, все компоненты распаяны, остается запрограммировать. По UART программировать не хотелось, поэтому решил использовать внутрисхемный программатор/отладчик ST-Link v2, а точнее его клон от неизвестного китайского производителя. 0, к ней производителем микроконтроллера распространяется библиотека Standard Peripherals Library + software pack. После поисков в интернете по запросу: “JTAG — SWD переходник” было выяснено, что линия SWDIO подключается к JTAG-TMS, а SWCLK к JTAG-TCK и “О чудо!” все заработало, в микроконтроллер прошилась тестовая программа. Keil его поддерживает “из коробки”, а вот микроконтроллер, хоть и в документации сказано, что поддерживает SWD интерфейс, но как и куда что подключать упомянуть забыли.

Видимо после перепрошивки линии порта JTAG-A переопределяются на другое функциональное назначение, хотя в программе порт B на котором находится JTAG-A даже не инициализировался. На этом радость закончилась, так как после прошивки микроконтроллер хоть и работал, но отладчиком видится перестал. При подключении к альтернативному JTAG интерфейсу, все заработало как часы. Разбираться в этом не хотелось, так как есть еще и JTAG-B. В дальнейшем его и будем использовать для программирования и отладки.

Дабы не изобретать велосипед возьмем кроссплатформенную свободно распространяемую библиотеку Freemodbus, и портируем ее для нашего микроконтроллера. Первой же поставленной себе задачей, было подключить контроллер к SCADA системе используя протокол Modbus.

Делается это простым двойным кликом мыши по файлу. Для создания проектов в Keil на микроконтроллере от Миландр нужно для начала установить software pack. Далее Keil все сделает сама.

Выбираем наш микроконтроллер и компоненты библиотеки которые нам понадобятся: И так создаём новый проект.

В дереве проекта создаем группу Modbus Slave и добавляем туда следующие файлы из библиотеки Freemodbus:

И не забываем в опциях проекта указать компилятору следующие пути к каталогам библиотеки.

Для этого нужно в файле portserial.c прописать функцию инициализации порта UART xMBPortSerialInit Теперь можно приступить конкретно к портированию библиотеки Freemodbus под наш микроконтроллер с использованием Standard Peripherals Library.

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

функцию записи и чтения:

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

void USART2_IRQHandler(void)
/* Событие при передаче байта ------------------------------------------------*/ if((UART_GetITStatus(MDR_UART2,UART_IT_TX)) !=RESET) { prvvUARTTxReadyISR( ); } }

Следом за этим правим файл portimer.c в котором настраивается таймер который формирует временные отчеты для отслеживания конца пакета протокола 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( );
}

В main.c добавим функции обработки регистров modbus, неиспользуемые регистры заглушим заглушками

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

На этом портирование закончено, осталось только в функции int main(void) инициализировать библиотеку и в цикле вызывать eMBPoll( );

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

В режиме отладки узнаем, что пакеты принимаются обрабатываются и программа зависает на инициализации передачи. Компилируем все без ошибок, но ничего не работает. После штудирования раздела “Описание работы UART” спецификации на микроконтроллер наткнулся на Примечание: При включении прерывания от передатчика UART, прерывание не вызывается, и программа уходит в бесконечный цикл.

В случае, если модуль и прерывания от него разрешены до осуществления записи данных в буфер FIFO передатчика, прерывание не формируется. Прерывание передатчика работает по фронту, а не по уровню сигнала. Прерывание возникает только при опустошении буфера FIFO.

Ну что же не беда, ищем где происходит начало передачи, в файле mbrtu.c находим строчки кода

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

и принудительно отправляем байт в передатчик UART, для этого добавляем строку: "xMBRTUTransmitFSM();" и все прекрасно начинает работать, пакеты бегают, регистры читаются, а дальше дело техники.

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть