Записная книжка разработчика

Мои проекты

Протокол Modbus в устройствах на базе микроконтроллеров. Часть 2.1. Программная поддержка протокола

| Comments

Для поддержки протокола Modbus RTU программа должна принимать символы, поступающие в порт и размещать их в буфере приёма. Признаком окончания сообщения служит тайм-аут, т.е. прекращение поступления символов в течение 3,5 - 4.5 длительностей передачи одиночного символа.

По окончании приёма сообщения управление должно передаваться специальному обработчику, который декодирует команду протокола, исполняет её, и формирует строку ответа, которая размещается в буфере передачи.

Передача ответа также ведётся с помощью прерываний. Начало передаваемого сообщения размещается в аппаратном буфере UART, при его исчерпании формируется прерывание, и буфер заполняется снова.

Работа с сообщениями ModbusASCII происходит аналогичным образом, за исключением того, что признаком конца приёма сообщения служит не тайм-аут,  а специальный символ конца сообщения.

В этой статье будет рассмотрен только режим Modbus RTU, как наиболее распространённый в настоящее время.

Итак, всю задачу можно условно разбить на три части, начиная с самого нижнего уровня:

  1. Функции работы с UART
  2. Функции, относящиеся к протоколу Modbus
  3. Функции, относящиеся к основному алгоритму работы контроллера (функции исполнения команд Modbus)

Функции работы с UART в микроконтроллере на ядре Cortex M3 (LPC1768)

Как и большинство микроконтроллеров, LPC1768 предусматривает три основных способа работы с UART: по опросу флагов, по прерыванию, и в режиме прямого доступа к памяти. Работа с UART путём опроса флагов означает, что программа должна ожидать наступления события, такого как поступление символа в буфер приёма или освобождение буфера передачи. Во время ожидания контроллер должен непрерывно, в цикле проверять состояние соответствующих регистров UART. Работа с UART по опросу флагов малопригодна для реальных приложений, обычно используется работа с UART по прерываниям. В данном примере будет рассмотрена работа с UART именно с помощью прерываний. Работа с UART в режиме прямого доступа к памяти несколько сложнее в реализации и (пока) рассматриваться не будет.

Микроконтроллер LPC1768 содержит 4 порта UART, и, если мы используем в устройстве несколько портов, то для каждого из них нам понадобится свой собственный обработчик прерывания и свой набор буферов приёма и передачи.

Следует отметить, что порты UART0 и UART1 в LPC1768 не абсолютно одинаковы, UART1 имеет специальный режим, упрощающий работу с портом RS-485, он будет рассмотрен в дальнейшем. Порты UART0, UART2 и UART3 идентичны и полностью взаимозаменяемы.

Содержимое заголовочного файла модуля LPC_Uart.h приведено ниже. Он содержит функции работы с UART0 и UART1. При необходимости, он может быть дополнен функциями работы с UART2 и UART3.

/*************************************************************************
*  UART functions
*  32bit.me (c)2010  
**************************************************************************/

#ifndef __LPC_UART_H
#define __LPC_UART_H

#include "type.h"

#define FIFODEEP    16

#define RXBUFSIZE  1024
#define TXBUFSIZE  1024

#define BD115200    115200
#define BD38400     38400
#define BD19200     19200
#define BD9600      9600

/* Uart line control register bit descriptions */
#define LCR_WORDLENTH_BIT         0
#define LCR_STOPBITSEL_BIT        2
#define LCR_PARITYENBALE_BIT      3
#define LCR_PARITYSEL_BIT         4
#define LCR_BREAKCONTROL_BIT      6
#define LCR_DLAB_BIT              7

// Uart Interrupt Identification
#define IIR_RSL                   0x3
#define IIR_RDA                   0x2
#define IIR_CTI                   0x6
#define IIR_THRE                  0x1

// Uart Interrupt Enable Type
#define IER_RBR                   0x1
#define IER_THRE                  0x2
#define IER_RLS                   0x4

/* Uart Receiver Errors*/
#define RC_FIFO_OVERRUN_ERR       0x1
#define RC_OVERRUN_ERR            0x2
#define RC_PARITY_ERR             0x4
#define RC_FRAMING_ERR            0x8
#define RC_BREAK_IND              0x10

typedef enum {
 UART0 = 0,
 UART1
} LPC_UartChanel_t;

// Word Lenth type
typedef enum {
 WordLength5 = 0,
 WordLength6,
 WordLength7,
 WordLength8
} LPC_Uart_WordLenth_t;

// Parity Select type
typedef enum {
 ParitySelOdd = 0,
 ParitySelEven,
 ParitySelStickHigh,
 ParitySelEvenLow
} LPC_Uart_ParitySelect_t;

// FIFO Rx Trigger Level type
typedef enum {
 FIFORXLEV0 = 0,    // 0x1
 FIFORXLEV1,        // 0x4
 FIFORXLEV2,        // 0x8
 FIFORXLEV3        // 0xe
} LPC_Uart_FIFORxTriggerLevel_t;

typedef struct {
 unsigned long BaudRate;            // Baud Rate

 LPC_Uart_WordLenth_t WordLenth;    // Frame format
 BOOL TwoStopBitsSelect;
 BOOL ParityEnable;
 LPC_Uart_ParitySelect_t ParitySelect;
} LPC_Uart_Config_t;

void SetUart0RxHandler(void (*_rx_handler)(char *buf, int length));

char Uart0Init(void);

int Uart0Transmit(int length);

char* GetUart0TxBuffer(void);

void SetUart1RxHandler(void (*_rx_handler)(char *buf, int length));

char Uart1Init(void);

int Uart1Transmit(int lenght);

char* GetUart1TxBuffer(void);

#endif //__LPC_UART_H

В следующей части будут рассмотрены функции работы с UART1 (функции работы с UART0 аналогичны, за исключением особенностей, которые будут оговорены далее).

Владимир Татарчевский