Русский
Русский
English
Статистика
Реклама

Портирование ModBus Slave RTUASCII на IAR AVR v3



Я уже десять лет не писал под AVR А вдруг разучился?! Для проверки я решил портировать библиотеку ModBus Slave RTU/ASCII без смс и регистрации на платформу IAR AVR, а также, по просьбам читателей, показать демку подключения к панели оператора Weintek.


Для решения этой задачи в кустах обнаружился рояль ноутбук IBM ThinkPad, нужным комплектом программного обеспечения.


Спаян шнурок для программатора AVReal.


В хламе найдена макетная плата с ATMega48. Фотография макетной платы на на первом рисунке.

Поехали!


Для портирования библиотеки ModBus Slave RTU/ASCII без смс и регистрации необходимо написать интерфейсы системного таймера и последовательного порта. У автора нездоровая привычка, писать низкоуровневый ввод/вывод для AVR на ассемблере. В нашем случае, я не буду отказывать себе в своих привычках.
Заголовочный файл systimer.h
#ifndef __SYSTIMER_H#define __SYSTIMER_H#ifdef __SYSTIMER_ASM#define CLKSysTimer (8000000/64)#else#include "main.h"//Инициализацияvoid InitSysClock(void);//время от запуска в милисекундахunsigned long Clock(void);#endif#endif


Файл systimer.asm
#define __SYSTIMER_ASM#include <iom48.h>#include "systimer.h"MODULE __systimerCOMMON INTVECORG TIMER0_COMPA_vect  rjmp tim0_compRSEG CODEtim0_comp:  in r10,SREG  inc r11  add r12,r11  dec r11  adc r13,r11  adc r14,r11  adc r15,r11  out SREG,r10  retiPUBLIC InitSysClockInitSysClock:  cli  clr r11  clr r12  clr r13  clr r14  clr r15  push r16  ldi r16,(0<<COM0A1)|(0<<COM0A0)|(0<<COM0B1)|(0<<COM0B0)|(1<<WGM01)|(0<<WGM00)  out TCCR0A,r16  ldi r16,(0<<FOC0A)|(0<<FOC0B)|(0<<WGM02)|(3<<CS00)  out TCCR0B,r16  ldi r16,(CLKSysTimer/1000)  out OCR0A,r16  ldi r16,(0<<OCIE0B)|(1<<OCIE0A)|(0<<TOIE0)  sts TIMSK0,r16  pop r16  retiPUBLIC ClockClock:  cli  movw r16,r12  movw r18,r14   retiENDMODEND


В качестве системного таймера используется TIMER0. В прерывании по совпадению таймера (COMPA), происходящем каждую милисекунду, инкрементируется четырехбайтная переменная находящаяся в регистрах r12-r15. Эти регистры не поддерживают работу с константами, поэтому для инкремента приходится использовать регистр r11. Регистр r10 используется для сохранения регистра состояния процессора. Перечисленные регистры зарезервированы в настройках компилятора.
Значение переменной r12-r15, через атомарную операцию считывается функцией Clock(), необходимой для работы библиотеки ModBus Slave RTU/ASCII.
Частота прерываний таймера определяется константой CLKSysTimer в заголовочном файле. Значение константы отношение тактовой частоты процессора к пределителю таймера.

Интерфейс последовательного порта.
Заголовочный файл uart.h
#ifndef __UART_H#define __UART_H#ifdef __UART_ASM#define CLK_Uart (8000000)#define UartSpeed (19200)#define FIFORX (32)#define FIFOTX (64)#else#include "main.h"void UartInit(void);unsigned short Inkey16Uart(void);void PutUart(unsigned char a);#endif#endif


Файл uart.asm
#define __UART_ASM#include <iom48.h>#include "uart.h"MODULE __uartrxtxRSEG NEAR_Zrxfifo: //Буфер FIFO  DS FIFORXrxHead://голова, пишем на голову  DS 1 rxTail://хвост, читаем с хвоста  DS 1 txfifo://Буфер FIFO  DS FIFOTXtxHead://голова, пишем на голову  DS 1 txTail://хвост, читаем с хвоста и в UART  DS 1 COMMON INTVECORG USART_RX_vect  rjmp uart_rxORG USART_TX_vect  rjmp uart_tx  RSEG CODE//void UartInit(void);PUBLIC UartInitUartInit:  sbi PORTD,0  cli  push r16  //обнуление указателей  clr r16  sts rxHead,r16  sts rxTail,r16  sts txHead,r16  sts txTail,r16  //Скорость передачи  ldi r16,LOW((CLK_Uart/8+UartSpeed/2)/UartSpeed-1)  sts UBRR0L,r16  ldi r16,HIGH((CLK_Uart/8+UartSpeed/2)/UartSpeed-1)  sts UBRR0H,r16  //Enable receiver and transmitter, разрешение прерываний  ldi r16,(1<<RXEN0)|(1<<TXEN0)|(1<<RXCIE0)|(1<<TXCIE0)  sts UCSR0B,r16  //Set frame format: 8data, 1stop bit, Parity No  ldi r16, (0<<UMSEL00)|(0<<UPM00)|(0<<USBS0)|(3<<UCSZ00)  sts UCSR0C,r16  //сброс флагов прерываний UART  lds r16,UCSR0A  ori r16,(1<<TXC0)|(1<<U2X0)   sts UCSR0A,r16  lds r16,UDR0  pop r16  reti//Обработчик прерывания по приемуuart_rx:  push r16  in r16,SREG  push r16  push XL  push XH//UART->FIFO    lds r16,rxHead  ldi XL,LOW(rxfifo)  ldi XH,HIGH(rxfifo)  add XL,r16  adc XH,r16  sub XH,r16  inc r16  andi r16,(FIFORX-1)  sts rxHead,r16  lds r16,UDR0  st X,r16  pop XH  pop XL      pop r16  out SREG,r16  pop r16  reti//unsigned short Inkey16Uart(void);//Если нет данных возвращает 0х0000, иначе возвращает 0х01ХХPUBLIC Inkey16UartInkey16Uart:  lds R17,rxHead  lds r16,rxTail  cp r16,r17  breq Inkey16Uart1  //читаем данные из FIFO    push XL  push XH  ldi XL,LOW(rxfifo)  ldi XH,HIGH(rxfifo)  add XL,r16  adc XH,r16  sub XH,r16    inc r16  andi r16,(FIFORX-1)  sts rxTail,r16    ld r16,X  pop XH  pop XL  ldi r17,1  retInkey16Uart1:  clr r16  clr r17  ret//обработчик прерывания по передачеuart_tx:  push r16  in r16,SREG  push r16  push r17  //проверяем наличие данных в буфере  lds r17,txHead  lds r16,txTail  cp r16,r17  brne uart_tx2    rjmp uart_tx_enduart_tx2://если данные есть - передаем    push XL  push XH  ldi XL,LOW(txfifo)  ldi XH,HIGH(txfifo)  add XL,r16  adc XH,r16  sub XH,r16    inc r16  andi r16,(FIFOTX-1)  sts txTail,r16    ld r16,X  sts UDR0,r16  pop XH  pop XLuart_tx_end:  pop r17  pop r16  out SREG,r16  pop r16  reti  //void PutUart(char a);PUBLIC PutUartPutUart:  push XL  push XH//проверяем наличие данных в буфере  lds XH,txHead  lds XL,txTail  cp XH,XL  brne PutUart1//проверякм регистр передачи  lds XL,UCSR0A  sbrs XL,UDRE0  rjmp PutUart1  sts UDR0,r16  pop XH  pop XL  retPutUart1://положить в txfifo[]    push r16  mov r16,XH  ldi XL,LOW(txfifo)  ldi XH,HIGH(txfifo)  add XL,r16  adc XH,r16  sub XH,r16  inc r16  andi r16,(FIFOTX-1)  sts txHead,r16  pop r16  st X,r16  pop XH  pop XL      retENDMODEND


Интерфейс последовательного порта реализован по классической схеме. Как на прием, так и на передачу реализован тип данных очередь на кольцевом буфере. Размер буфера приема и передачи определяется константами FIFORX, FIFOTX соответственно. В целях экономии вычислительных ресурсов процессора, размер буферов приема и передачи должен быть кратен 2^N (2,4,8,16,32...), но не больше 256.
Скорость приема/передачи последовательного порта определяется константой UartSpeed. При тактовой частоте микроконтроллера (определяется константой CLK_Uart) 8МГц, то есть, при использовании внутреннего RC-генератора нет возможности использовать высокие скорости передачи.

При попытке скомпилировать файл библиотеки modbus.c, IAR заругался страшными словами. Компилятор IAR AVR не умеет много чего из стандарта С99. Он также не умеет, при использовании модификатора const, размещать объекты в памяти программ, для этого служит специальный модификатор __flash. Пришлось потратить несколько минут для приведения кода в соответствие требованиям компилятора.
В файле библиотеки modbus.h необходимо определить макросы вызова функций последовательного интерфейса и системного таймера.
//Системный таймер, инкрементируется каждую милисекунду#define ModBusSysTimer Clock()//Запись байта в поток последовательного порта - void ModBusPUT(unsigned char A)#define ModBusPUT(A) PutUart(A) //Чтение байта из потока последовательного порта, - unsigned short ModBusGET(void)//Если нет данных возвращает 0х0000, иначе возвращает 0х01ХХ#define ModBusGET() Inkey16Uart()

А также определить количество дискретных входов/выходов, регистров для чтения и регистров для чтения/записи. На этом портирование библиотеки можно считать законченным.

Демка


Для демонстрации возможностей библиотеки ModBus Slave RTU/ASCII подключим наше устройство к панели оператора Weintek. В микроконтроллере организованы часы, значения часов, минут секунд выводятся в регистры Modbus, код содержится в файле ModBus2Prg.c:
void Prg2ModBusOutReg(void)  {//заполнение регистров 4Х регистры для чтения/записи  ModBusOutReg[0]=Seconds;  ModBusOutReg[1]=Minutes;  ModBusOutReg[2]=Hours;   return;  }void Prg2ModBusInReg(void)  {//заполнение регистов 3Х регистры для чтения  ModBusInReg[0]=Seconds;  ModBusInReg[1]=Minutes;  ModBusInReg[2]=Hours;    return;  }

Через регистры чтения/записи можно произвести установку часов:
void ModBus2PrgOutReg(void)  {//чтение регистров 4Х регистры для чтения/записи  Seconds=ModBusOutReg[0];  Minutes=ModBusOutReg[1];  Hours=ModBusOutReg[2];   return;  }

Дискретные входы/выходы Modbus подключены к портам вывода микроконтроллера. Так же к дискретному входу 4 подключен счетчик полусекунд:
void Prg2ModBusOutBit(void)  {//заполнение регистров дискретных выходов  ModBusOutBit[0].bit0=PORTC_Bit1;  ModBusOutBit[0].bit1=PORTC_Bit2;  ModBusOutBit[0].bit2=PORTC_Bit3;  ModBusOutBit[0].bit3=PORTC_Bit4;  return;  }void Prg2ModBusInBit(void)  {//заполнение регистров дискретных входов  ModBusInBit[0].bit0=PORTC_Bit1;  ModBusInBit[0].bit1=PORTC_Bit2;  ModBusInBit[0].bit2=PORTC_Bit3;  ModBusInBit[0].bit3=PORTC_Bit4;  ModBusInBit[0].bit4=PoluSeconds;  return;  }

Через дискретные выходы можно управлять состоянием портов микроконтроллера:
void ModBus2PrgOutBit(void)  {//чтение регистров дискретных выходов  PORTC_Bit1=ModBusOutBit[0].bit0;  PORTC_Bit2=ModBusOutBit[0].bit1;  PORTC_Bit3=ModBusOutBit[0].bit2;  PORTC_Bit4=ModBusOutBit[0].bit3;  return;  }

Программное обеспечение панели разрабатывается в среде EasyBuilder Pro v5.

Подключаем микроконтроллер через USB преобразователь к компьютеру, указываем в настройках проекта панели протокол обмена Modbud RTU и настройки COM-порта через который произошло подключение. Запускаем онлайн симуляцию панели.

Код демки с использованием библиотеки ModBus Slave RTU/ASCII со всеми опциями, после компиляции IAR AVR v3 с оптимизацией по скорости оказался на удивление компактным. Он занимает 3024 байта памяти программ, 398 байт памяти данных. Искренне надеюсь, что библиотека ModBus Slave RTU/ASCII найдет широкое применение для разработки Modbus устройств на маломощных микроконтроллерах.

Проект на GitHub
Видео демки
Источник: habr.com
К списку статей
Опубликовано: 27.11.2020 16:12:57
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Промышленное программирование

Разработка робототехники

Программирование микроконтроллеров

Разработка под arduino

Производство и разработка электроники

Modbus

Avr

Weintek

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru