Работа с периферией
В составе любого процессора для встраиваемых применений обязательно присутствует набор периферийных портов, необходимых для связи с внешними устройствами. Это может быть порт UART для вывода пользовательской консоли, или SPI для связи с микросхемой-датчиком, или Ethernet для работы в составе локальной сети. Вариантов множество, но все они имеют общую черту: каждое такое устройство не входит в состав процессорного ядра и управляется с помощью набора регистров, как правило, отображаемых в соответствующую область адресного пространства процессора. С точки зрения программиста, любое периферийное устройство — это набор программно-доступных периферийных регистров, отображаемых в общее адресное пространство. Как правило, регистры делятся на три группы:
- управляющие регистры — позволяют задать необходимый режим работы;
- статусные регистры — позволяют отслеживать состояние порта;
- буферные регистры — позволяют принимать и передавать данные через данный порт.
Фактически, любая работа с периферийным портом на программном уровне выглядит так.
Для передачи данных:
- настройка порта (скорость передачи, длина передаваемого слова и так далее);
- запись данных в передающий буфер;
- чтение статусного регистра в ожидании установки флага, сигнализирующего об окончании передачи данных, находящихся в буфере, или о наличии свободного места в буфере. В любом случае, флаг будет обозначать — «можно записывать следующую порцию данных в буферный регистр».
Для приема данных:
- настройка порта (скорость передачи, длина передаваемого слова и так далее);
- чтение статусного регистра в ожидании установки флага, сигнализирующего о наличии данных в приемном буфере;
- вычитывание принятых данных из приемного буфера и их обработка.
«После» приемных или передающих буферов устройств находятся устройства, реа лизующие непосредственно трансляцию данных между буфером и физическим интерфейсом соответствующего порта. Скажем, для порта UART за блоками приемных и передающих FIFO находятся соответственно приемный и передающий сдвиговые регистры, реализующие прием/передачу байта данных в сопровождении необходимого количества стартовых и стоповых битов, а также, при необходимости — бита четности.
Работа с портом UART
Рассмотрим настройку порта UART в любом процессоре архитектуры MIPS. Она осуществляется с помощью следующей функции:
// Инициализация UART0
// варьируется только скорость обмена
// остальные параметры не задаются
// длина слова - 8 бит
// стоп-бит - 1
// четность не контролируется
// управление потоком данных отсутствует
// BaudRate - скорость обмена
// Freq - частота процессора
void UART_conf(int BaudRate, int Frq) {
int dlm;
dlm = (Frq)/(BaudRate*16);
LCR_UART0 = (int)0x80;
SCLR_UART0 = 0;
DLL_UART0 = dlm&0xFF;
DLM_UART0 = (dlm>>8);
LCR_UART0 = 3;
} // UART_conf
Здесь мы производим следующую последовательность действий:
- устанавливаем в единицу бит LCR[7] порта UART0, тем самым переводя порт в режим установки делителей частоты (в этом режиме недоступны регистры приемного и передающего буфера, а значит, невозможна передача и прием данных);
- задаем значения делителей частоты в регистрах SCLR, DLL, DLM;
- переключаем порт обратно в рабочий режим, устанавливая настройки порта в соответствии с описанием регистра LCR.
Передача символа реализуется так:
void UART_sendByte(char Sym) {
// пишем символ в порт
THR_UART0 = Sym;
// ждем, пока он будет отправлен
while ( ( LSR_UART0 & 0x60 ) != 0x60 ) ;
} // UART_sendByte
В первой строке функции символ, подлежащий отправке, записывается в буфер передатчика. Во второй строке мы переходим в цикл, где происходит постоянное чтение статусного регистра LSR и проверка состояний разрядов THRE и TEMT.
Первый из них сообщает состояние буфера отправки. Если он выставлен в единицу —
это значит, что данные из него переданы в сдвиговый регистр, осуществляющий отправку, и регистр THR
готов к получению новых данных. Второй разряд (TEMT) сообщает о состоянии сдвигового регистра TSR,
осуществляющ его непосредственно отправку. Если оба этих разряда имеют значение "1" — значит,
записанный ранее байт уже передан в линию UART. Именно этого момента мы дожидаемся с помощью цикла
while()
.
Функция, реализующая отправку строки:
void UART_sendStr(char *Str) {
while (*Str != 0)
{
UART_sendByte(*Str++);
}
}
Таким образом, мы ждем флага, говорящего нам о том, что байт отправлен, и отправляем следующий. Полноценный код отсылки строки и приема через UART, можно найти в примерах UART, UART_echo, UART_FIFO_echo в составе среды разработки MCStudio 3M или MCStudio 4. Применительно к отладочному модулю NVCom-02TEM-3U, для работы с UART необходимо установить драйвер для микросхемы USB-UART CP2102.
Оригинальный драйвер можно скачать с сайта разработчика, во вкладке SOFTWARE&TOOLS. В зависимости от операционный системы пользователя можно выбрать CP210x Windows Drivers, CP210x Software package for Linux, CP210x Software package for Mac, includes VCP Drivers.
Для приема данных по COM порту можно использовать свободно распространяемый клиент PuTTy со следующими настройками:
- Линия для подключения: COMx
- Скорость: 9600 бод
- Биты данных: 8
- Стоп биты: 1
- Четность: нет
- Управление потоком: нет
Чтобы установить на каком порту подключен USB-UART, необходимо зайти в диспетчер устройств и узнать, какое устройство появляется в списке при подключении нашего устройства к USB-порту и исчезает из этого списка при отключении устройства от USB-порта.
Последовательность действий при работе с примерами проектов такая:
- запустить отладку «Debug Configurations… → Debug»;
- открыть окно клиента PuTTy с выполненными настройками;
При запуске проекта UART в окне клиента появятся строки слова “Hello!” в бесконечном цикле, в проектах UART_fifo_echo и UART_echo в окно клиента выводятся символы, набранные пользователем на клавиатуре.
Работа с портом MFBSP в режиме линкового интерфейса LPORT
MFBSP в режиме линкового порта (LPORT) в процессоре 1892ВМ10Я может работать только в одном направлении — передатчик либо приемник, в зависимости от направления обмена данными. Рассмотрим алгоритм обмена между периферийными портами, реализованный в примере MFBSP_LPORT (он находится в составе среды разработки MCStudio 3M/4), иллюстрирующий простой обмен между LPORT2 и LPORT3:
- Объявляются два массива. Один из них заполняются нулями (туда будут приниматься данные), второй заполняется инкрементными значениями (данные из этого массива будут передаваться).
- Настройка портов на обмен.
- Отправка символа в буфер передачи.
- Ожидание приёма символа.
- Чтение символа в буфер приема.
- Проверка корректности принятых данных, по результатам формируется значение флага корректности данных.
- Чтобы увидеть результат работы программы, нужно в среде MCStudio 4 поставить точку останова после окончания проверки, как показано на рисунке ниже, создать конфигурацию отладки и запустить программу на исполнение. После останова программы во вкладке «Variables» найти значение флага корректности приемного массива и убедиться, что он равен единице, как показано на рисунке ниже.
Рассмотрим описанные выше пункты в виде кода на Си с комментариями. Настройка портов на обмен осуществляется инициализацией регистров управления и состояния и заданием частоты обмена:
// LPORT2 передает:
CSR_MFBSP2 = ( (LCLK_RATE&0x1E)<<10 ) | // LCLK_RATE[4:1]
( (LCLK_RATE&1) << 2 ) | // LCLK_RATE[0]
( 1<<6) | // LDW = 1 (длина слова - 8 бит)
( 1<<1 ) | // LTRAN = 1 (передатчик)
1; // LEN = 1;
// LPORT3 принимает:
CSR_MFBSP3 = ( (LCLK_RATE&0x1E)<<10 ) | // LCLK_RATE[4:1]
( (LCLK_RATE&1) << 2 ) | // LCLK_RATE[0]
( 1<<6) | // LDW = 1 (длина слова - 8 бит)
1; // LEN = 1;
LCLK_RATE = (FREQ/(2*LPORT_FRQ)) - 1;
Приёмник осуществляет синхронный приём параллельно-последовательного кода с внешних выводов схемы и
запись принятых данных в буфер LPORT.
Передатчик осуществляет чтение данных из буфера LPORT и синхронную выдачу их параллельно
последовательным кодом на внешние выводы схемы. Запись передаваемых данных в буфер LPORT
осуществляется при записи элемента массива OutputArray[i]
по адресу псевдорегистра TX_MFBSP.
Бит [3] регистра управления и состояния CSR порта-приёмника MFBSP в режиме LPORT отображает
состояние буфера приема, именно изменения его состояния с нуля на единицу мы ожидаем в цикле while
кода, приведенного ниже. Чтение принятого элемента массива InputArray[i] из буфера LPORT
осущест вляется при чтении по адресу псевдорегистра RX_MFBSP:
for (i=0;i<ARRAY_LEN;i++)
{
TX_MFBSP2=OutputArray[i]; //передача очередного значения в буфер
while (!(CSR_MFBSP3 & 1<<4)); //ожидание получения новых данных
InputArray[i]=RX_MFBSP3; //чтение полученного значения из буфера
}