Глава 12
Ох уж эти биты!
Параллельная передача данных может осуществляться с высокой скоростью и требует минимальных программных затрат для реализации. Однако имеется множество приложений, в которых параллельная передача данных неприменима либо из-за удорожания аппаратной части (см., например, Рис. 11.12 на стр. 350), либо, что встречается гораздо чаще, по причине значительного удаления узлов друг от друга. В последнем случае организация множества коммуникационных каналов вместе с соответствующим интерфейсным оборудованием невозможна в принципе или же требует неоправданных затрат. В таких случаях на помощь может прийти последовательная передача данных, при которой данные пересылаются побитно (по одному биту за раз) и объединяются в приемном устройстве в исходные байты. Здесь можно провести сравнение с параллельным портом персонального компьютера, обычно используемого для подключения локальных периферийных устройств (например, принтера), и последовательным или USB-портом, которые часто используются совместно с модемом для выхода через телефонную линию в сеть Интернет.
В данной главе мы познакомимся с различными средствами, использующимися для последовательной передачи данных (в основе всех этих средств лежат специализированные сдвиговые регистры), а также с серийно выпускаемыми микросхемами, поддерживающими стандартные протоколы обмена. После прочтения этой главы вы:
• Осознаете необходимость в последовательной передаче данных.
• Сможете разрабатывать последовательные порты и сопутствующее программное обеспечение для обмена со стандартными параллельными периферийными устройствами.
• Научитесь работать с последовательными периферийными устройствами, поддерживающими протоколы SPI™ и I2С.
• Поймете необходимость асинхронной последовательной передачи данных и сможете писать программные драйверы, поддерживающие этот протокол.
• Научитесь использовать интегрированный модуль универсального синхронно-асинхронного приемопередатчика (USART) в асинхронном режиме.
• Поймете причины, по которым возникает необходимость буферизации в устройствах, передающих данные на большие расстояния.
В качестве примера последовательной передачи данных рассмотрим смарт-карту, которая наверняка имеется в вашем кошельке. В каждую такую карту встроен микроконтроллер, обычно 8-битный, который, собственно, и делает карту «умной» (smart). Себестоимость изготовления этих карт не должна превышать 1 долл., причем большая часть этих денег уходит на коррозионно-стойкие контакты с золотым покрытием, через которые на микроконтроллер подается питание и тактовый сигнал, когда карта устанавливается в считывающее устройство. Чтобы снизить требования к точности изготовления механических узлов считывателя и, соответственно, увеличить его надежность, количество контактов необходимо свести к минимуму, а их размеры, наоборот, должны быть максимально возможными.
Стандартная цоколевка такого микроконтроллера показана на Рис. 12.1. Как видно из рисунка, два контакта используются для подачи напряжения питания, еще два контакта предназначены для подачи тактового сигнала и сигнала сброса, и всего лишь один контакт используется для побитовой передачи данных в обоих направлениях. Несмотря на то что такой обмен происходит достаточно медленно, на фоне скорости системы «человек — машина» он незаметен. Кроме того, связь между считывателем/банкоматом и центральным компьютером, который может быть расположен на расстоянии нескольких тысяч миль/километров, обычно осуществляется по одной телефонной линии.
Рис. 12.1. Смарт-карта
Вернемся к схеме параллельного интерфейса с 3-разрядным 7-сегментным дисплеем, приведенной на Рис. 11.15 (стр. 361), в которой используются оба параллельных порта А и В. Хотя это вполне рабочая схема, в ней задействована большая часть выводов 18-выводных микроконтроллеров. Поскольку в данном случае скорость не очень критична, можно использовать более медленный способ передачи данных.
Взгляните на схему с использованием последовательного интерфейса, приведенную на Рис. 12.2. В этой схеме используются только два вывода порта. Один, называемый SDO (Serial Data Out — выход последовательных данных), используется для побитовой передачи данных. Второй, названный SCK (Serial ClocK — последовательный тактовый сигнал), используется для одновременного тактирования трех сдвиговых регистров, осуществляя, таким образом, побитовый сдвиг данных вправо аналогично тому, как это было изображено на Рис. 3.8 (стр. 78).
Рис. 12.2. Последовательный интерфейс с 3-разрядным 7-сегментным дисплеем
Каждый индикатор дисплея подключен к своему 8-битному сдвиговому регистру 74НСТ164 (см. Рис. 2.22 на стр. 51). Эта микросхема имеет тактовый вход С1 (активный фронт — нарастающий) и два входа данных, объединенных по И. Один из этих входов может использоваться для стробирования второго, но в нашем примере они объединены, образуя один вход данных. Также имеется вход сброса с активным НИЗКИМ уровнем для очистки содержимого регистра (в нашей схеме на него подается ВЫСОКИЙ уровень). При необходимости для управления этим входом можно задействовать еще один вывод микроконтроллера.
Чтобы сменить изображение на дисплее, необходимо задвинуть в указанную цепочку регистров 24 бита данных. Чтобы разобраться, каким образом это можно сделать, обратимся снова к процедуре управления 7-сегментным индикатором из Программы 11.9 (стр. 363), в которой осуществляется преобразование двоичного числа в набор BCD-разрядов, хранящихся в регистрах HUNDREDS, TENS и UNITS. Эти значения преобразовывались в 7-сегментный код, который затем выставлялся на 8-битную шину.
Чтобы преобразовать этот процесс к последовательному виду, нам потребуется написать подпрограмму, которая будет по очереди выдавать все биты регистра, скажем DATA_OUT, на вывод RAO/SDO, начиная с самого левого (старшего) бита. Одновременно на выводе RA1/SCK будут формироваться тактовые импульсы для загрузки этих битов в регистры. Алгоритм работы данной подпрограммы будет следующим:
1. Выставить на SCK НИЗКИЙ уровень.
2. COUNT = 8.
3. ПОКА COUNT > 0, ВЫПОЛНЯТЬ:
а) Копировать старший бит DATA_OUT в SDO.
б) Сдвинуть DATA_OUT на один бит влево.
в) Сформировать импульс на SCK.
г) Декрементировать COUNT.
В Программе 12.1 содержится две подпрограммы. Первая из них, названная DISPLAY, очень похожа на ту, что была написана нами в Программе 11.9, поскольку точно так же вызывает подпрограмму BIN_2_BCD и копирует значения кодов 7-сегментного индикатора в интерфейсный регистр. В данном случае сначала загружается значение единиц (поскольку этот байт в конце концов будет задвинут в самый последний регистр цепочки), затем десятков и, наконец, сотен.
Программа 12.1. Отображение трехразрядного десятичного числа с использованием последовательной передачи данных
include "p16f627а. inc"
SDO equ 0
SCK equ 1
DISPLAY bcf PORTA,SCK ; Инициализируем линию SCK
movf BINARY,w ; Берем двоичное значение
call BIN_2_BCD ; Преобразуем его в три BCD разряда
movf UNITS,w ; Берем число единиц
call SVN_SEG ; Преобразуем в 7-сегментный код
movwf DATA_OUT ; Копируем в регистр последовательной передачи
call SPI_WRITE ; Выдвигаем его
movf TENS,W ; Берем число десятков
call SVN_SEG ; Преобразуем в 7-сегментный код
movwf DATA_OUT ; Копируем в регистр последовательной передачи
call SPI_WRITE ; Выдвигаем его
movf HUNDREDS,w ; Берем число сотен
call SVN_SEG ; Преобразуем в 7-сегментный код
movwf DATA_OUT ; Копируем в регистр последовательной передачи
call SPI_WRITE ; Выдвигаем его
return
; ********************
; * ФУНКЦИЯ: Побитно передает байт данных, начиная со старшего бита *
; * ВХОД: Байт данных в DATA_OUT *
; * ВЫХОД: DATA_OUT обнуляется *
; ********************
; Задача 1
SPI_WRITE
bcf PORTA,SCK ; В режиме ожидания на линии SCK — НИЗКИЙ
; Задача 2
movlw 8 ; Инициализируем счетчик цикла
movwf COUNT
; Задача 3, a и 3, б
LOOP bcf PORTA,SDO ; Выставляем на линию данных 0
btfsc DATA_OUT,7 ; Пропускаем, ЕСЛИ старший бит = 0
bsf PORTA,SDO ; ИНАЧЕ выставляем на линию данных 1
rlf DATA_OUT,f ; Сдвигаем байт данных на один бит влево
; Задача 3, в
bsf PORTA,SCK ; Формируем тактовый импульс
bcf PORTA,SCK
;Задача 3,г
decfsz COUNT,f ; Декрементируем счетчик
goto LOOP ; и повторяем, пока он не станет равным 0
return
Собственно последовательная передача данных осуществляется подпрограммой SPI_WRITE, работающей по приведенному выше алгоритму. В подпрограмме проверяется 7-й бит содержимого регистра DATA_OUT и в соответствии с его значением на вывод RA0 выставляется ВЫСОКИЙ или НИЗКИЙ уровень. Затем на выводе RA1 формируется положительный импульс для загрузки очередного бита в цепочку сдвиговых регистров, после чего байт данных сдвигается влево. Этот процесс повторяется 8 раз. На все это требуется не более 87 машинных циклов (конкретная цифра слегка зависит от значения байта данных). Таким образом, на полное обновление изображения 3-разрядного дисплея уйдет около 120 мкс при частоте процессора 8 МГц (не учитывая время, затраченное на преобразование данных).
В Программе 12.2 приведена одна из возможных реализаций данной подпрограммы на языке Си. Функция spi_write () 8 раз выдает 7-й бит переданного ей байта данных на вывод SDO и сдвигает значение этого байта влево. Предполагается, что оба вывода последовательного интерфейса SPI уже определены как соответствующие линии порта ввода/вывода микроконтроллера.
Программа 12.2. Реализация подпрограммы SPI_write на Си
void spi_write(int datum)
{
int k;
for(k=0;k<8;k++)
{
if((datum & 0x80)) {SDO = 1;} /* Проверяем 7-й бит и ЕСЛИ TRUE, выставляем 1 */
else {SDO = 0;} /* ИНАЧЕ выставляем 0 */
SCK = 1 ; /* Загружаем бит в цепочку регистров */
SCK = 0;
datum = datum <<1 ; /* Сдвигаем байт данных влево и повторяем 8 раз */
}
}
При работе с длинной цепочкой сдвиговых регистров скорость загрузки данных можно немного увеличить, если выделить каждому регистру собственные линии данных, при этом тактовые входы всех регистров будут подключены к выводу SCK. Также можно подключить входы данных всех регистров к одной-единственной линии данных и управлять регистрами с помощью отдельных сигналов разрешения. Последний способ использован в схеме, показанной на Рис. 12.7.
У нашей схемы, использующей сдвиговые регистры, есть один недостаток: значения, появляющиеся на выходах регистров во время их загрузки (в нашем случае в течение 23 тактовых импульсов), некорректны. Разумеется, в данном конкретном случае из-за инерционности зрения мы просто не заметим эти кратковременные изменения в свечении индикаторов. Однако иногда такое поведение схемы оказывается неприемлемым, поэтому к выходам сдвиговых регистров необходимо подключить буферы на D-триггерах или защелках. Загрузка содержимого регистров в эти буферы будет осуществляться после завершения передачи данных, в результате чего изображение на дисплее будет меняться только единожды.
Чтобы не использовать отдельные буферные регистры, в большинстве устройств, рассчитанных на последовательную передачу данных, имеется встроенный регистр с параллельным входом/выходом. В качестве примера можно привести микросхему 74НСТ595, изображенную на Рис. 12.3. Эта микросхема представляет собой стробируемый сдвиговый регистр, на выходе которого имеется встроенный 8-битный PIPO-регистр. По нарастающему фронту сигнала на выводе RCK (Register ClocK) содержимое сдвигового регистра выставляется на параллельные выходы. В микросхеме также выведен выход последнего триггера сдвигового регистра, что позволяет объединять эти регистры в цепочку любой длины. В этом случае на все выводы RCK может подаваться один и тот же стробирующий импульс для одновременного обновления выходов всех микросхем.
Примером ситуации, когда пульсации данных могут быть нежелательными, является преобразование цифровых данных в аналоговый сигнал. В схеме на Рис. 12.3 преобразование осуществляется с помощью микросхемы ЦАП DAC0800 фирмы National Semiconductor. Выходное налоговое напряжение является линейной функцией от 8-битного цифрового входа и изменяется от —9.96 В для входного значения Ь’00000000’ до +9.96 В для значения Ь’11111111’ (см. Рис. 14.17 на стр. 527).
Рис. 12.3. Последовательный интерфейс с микросхемой ЦАП, реализованный посредством сдвигового регистра 74НСТ595
Благодаря наличию регистра 74НСТ595 состояние входа ЦАП не меняется до тех пор, пока не будет загружен новый байт и микроконтроллер не сформирует на выводе RCK положительный импульс. Таким образом, обеспечивается отсутствие шумов в выходном аналоговом сигнале.
Аналогичным образом можно реализовать прием данных в последовательном режиме, используя сдвиговые регистры с параллельным входом и последовательным выходом (PISO-регистры). Схема, приведенная на Рис. 12.4, представляет собой последовательный вариант системы охранной сигнализации с Рис. 11.12 (стр. 350), использующий только три линии для подключения всех восьми групп датчиков. Это гораздо меньше 16 линий, задействованных нами в исходной схеме.
Каждая группа датчиков подключена к 8-битному сдвиговому PISO-регистру 74НСТ165. При этом последовательный выход каждого регистра подключен к последовательному входу следующего регистра. После загрузки данных в регистр их можно будет побитно передать на вывод RA1 порта A (SDI). В данном конкретном случае многозонной системы охранной сигнализации после каждого восьмого сдвига полученный байт можно проверять на ненулевое значение и осуществлять соответствующие действия.
Рис. 12.4. Вариант многозонной системы охранной сигнализации с последовательным интерфейсом
Для управления отображением активной зоны в схеме на Рис. 12.4 тоже используется один выход микроконтроллера. Поскольку и входной (SDI), и выходной (SDO) каналы используют один и тот же тактовый сигнал SCK, то одновременно с приемом данных будет осуществляться и их передача. И наоборот, передача данных через выходной порт приведет к побитному приему данных из регистров зон. В данном случае это не страшно, поскольку микросекундные изменения в свечении ламп совершенно незаметны, и после загрузки в регистр индикаторов требуемого значения все нормализуется. Когда же такое взаимное влияние операций приема и передачи нежелательно, то либо во время считывания данных с вывода SDI на выводе SDO должны всегда присутствовать требуемые данные, либо необходимо использовать стробируемый регистр, например 74НСТ595, для одновременного вывода всех битов данных. В качестве альтернативного решения можно также задействовать отдельные линии тактовых сигналов.
Подпрограмма для работы с последовательным интерфейсом SPI_READ является точной противоположностью подпрограммы SPI_WRITE, код которой был приведен в Программе 12.1, и реализует следующий алгоритм:
1. Выставить на SCK НИЗКИЙ уровень.
2. COUNT = 8.
3. ПОКА COUNT > 0, ВЫПОЛНЯТЬ:
а) Сформировать на линии SCK импульс
б) Сдвинуть содержимое регистра DATA_IN на один бит влево.
в) Скопировать значение с вывода SDI в старший бит регистра DATA_IN.
г) Декрементировать COUNT.
Этот алгоритм похож на приведенный ранее (см. стр. 371), за исключением того, что регистр DATA_IN сдвигается влево, а состояние вывода SDI становится значением 0-го бита. После восьмого цикла «такт — сдвиг — проверка» содержимое регистра DATA_IN представляет собой байт данных, считанный с последовательного порта. При этом первый принятый бит окажется старшим битом итогового значения.
Подпрограмма SPI_READ, текст которой приведен в Программе 12.3, похожа на подпрограмму вывода SPI_WRITE из Программы 12.1. И их действительно можно объединить для того, чтобы одновременно передавать и принимать данные. Такой тип обмена называется полнодуплексным (или просто дуплексным) в отличие от полудуплексного, при котором передача информации в каждый момент времени осуществляется только в одном направлении. Последовательный обмен, при котором поток данных может передаваться только в одном фиксированном направлении, называется симплексным.
Программа 12.3. Подпрограмма приема байта по последовательному каналу
; ***********************
; * ФУНКЦИЯ: Побитно принимает байт данных, начиная со старшего бита *
; * ВХОД: Нет *
; * ВЫХОД: Принятый байт в DATA_IN; COUNT = 0 *
; ***********************
; Задача 1: Выставляем на SCK НИЗКИЙ уровень
SPI_READ
bcf PORTA,SCK ; В режиме ожидания на линии SCK — НИЗКИЙ уровень
; Задача 2: COUNT=8
movlw 8 ; Инициализируем счетчик цикла
movwf COUNT
; Задача 3: ПОКА COUNT>0, ВЫПОЛНЯТЬ:
; Задача 3, а : Формируем импульс SCK
SER_IN_LOOP
bsf PORTA,SCK
bcf PORTA,SCK
; Задача 3, б : Сдвигаем байт данных влево
bcf STATUS,С ; Обнуляем флаг переноса
rlf DATA_IN,f ; Сдвигаем байт влево
; Задача 3, в : ЕСЛИ SDI = 1, ТО устанавливаем 0-й бит (самый правый)
btf sc PORTA,SDI ; Пропускаем, ЕСЛИ SDI == 0
bsf DATA_IN,0 ; ИНАЧЕ заносим в 0-й бит 1
; Задача 3, г : Декрементируем COUNT и повторяем задачу 3, пока COUNT > 0
decfsz COUNT,f; Декрементируем счетчик
goto SER_IN_LOOP; и повторяем, пока он не станет равным 0
return
Си-вариант подпрограммы, приведенный в Программе 12.4, использует тот же алгоритм, что и его ассемблерный предшественник. Обратите внимание, как для установки 0-го бита переменной DATA_IN используется Си-оператор ИЛИ (|) с константой Ь’00000001’. Аналогично, операция И с константой b’11111110’ сбрасывает 0-й бит. В компиляторе CCS имеются специальные нестандартные функции bset (DATA__IN, 0) и bclr (DATA_IN, 0), которые можно использовать для установки или сброса любого бита переменной и которые при необходимости изменения единственного бита часто более эффективны, чем использование логических операторов.
Программа 12.4. Реализация подпрограммы SPI_READ на Си
unsigned int spi_read()
{
int k;
for(k=0;k<8;k++) /* Повторяем 8 раз */
{
SCK =1; /* Формируем тактовый импульс, по которому */
SCK =0; /* ведомый передает бит на SDI */
DATA_IN = DATA_IN <<1; /* Сдвигаем на один бит влево */
if(SDI)
{DATA_IN = DATA_IN I 0x01;} /* Сбрасываем бит, если SDI =0 */
else
{DATA_IN = DATA_IN & 0xFE;} /* ИНАЧЕ устанавливаем его */
}
return DATA_IN /* Возвращаем принятый байт */
}
Последовательный протокол, как две капли воды похожий на только что рассмотренный нами, известен как последовательный периферийный интерфейс (SPI™). Существует еще один похожий последовательный протокол — Microwire, но он несколько отличается от рассмотренного.
Интерфейс SPI имеется в большинстве микроконтроллеров, и он в достаточной степени стандартизован, чтобы производители могли выпускать широкий ассортимент микросхем, предназначенных для непосредственного (без использования дополнительных сдвиговых регистров) подключения к этой шине.
Возьмем в качестве примера микросхему сдвоенного цифро-аналогового преобразователя (ЦАП) МАХ549А, показанную на Рис. 12.5, которая работает при напряжении питания от +2.5 до +5.5 В. Типичный ток потребления в рабочем режиме составляет около 150 мкА/канал при напряжении питания 5 В. Кроме того, один или оба модуля ЦАП можно отключить для уменьшения тока потребления до уровня менее 1 мкА. Максимальная частота шины SPI составляет 12.5 МГц. И все эти возможности заключены в крошечном 8-выводном корпусе — сравните с 20-выводной микросхемой МАХ506, изображенной на Рис. 14.16 (стр. 526), которая рассчитана на подключение к параллельному порту микроконтроллера.
Из упрощенной функциональной схемы МАХ549А, приведенной на Рис. 12.5, видно, что в микросхеме имеется встроенный 16-битный сдвиговый регистр, тактовый вход которого соединен с выводом SCLK микросхемы, а вход данных — с выводом DIN. Поэтому данные в этот регистр могут загружаться в соответствии с обычным протоколом SPI. Дополнительные восемь разрядов регистра используются для хранения четырех управляющих битов, выполняющих следующие функции:
∙ А0
Разрешает работу входного PIPO-регистра канала А, тактирование которого осуществляется по нарастающему фронту сигнала на выводе .
∙ А1
Разрешает работу входного PIPO-регистра канала В, тактирование которого осуществляется по нарастающему фронту сигнала на выводе .
∙ С1
Управляет работой обоих регистров ЦАП; при установленном бите содержимое этих регистров одновременно обновляется по нарастающему фронту сигнала на выводе .
Рис. 12.5. Микросхема сдвоенного ЦАП с интерфейсом SPI МАХ549А (Maxim)
∙ С2
Установка этого бита переводит ЦАП, определяемый битами А0 и/или А1, в «спящий режим». При этом источник опорного напряжения Vref отключается от резистивной цепи выбранного ЦАП (см. Рис. 14.14 на стр. 516), в результате чего ток потребления ЦАП уменьшается до значения менее 1 мкА (содержимое внутренних регистров ЦАП остается неизменным).
Между каждым из модулей ЦАП и сдвиговым регистром имеется 2-уровневый регистровый конвейер. На первом уровне расположены регистры INREGx, разрешение которых осуществляется установкой бита А0 (канал А) или А1 (канал В) в 1. При установленном бите данные, находящиеся в 1-м байте сдвигового регистра, можно загрузить в регистр, подав на вывод отрицательный импульс. Однако это значение не появится на параллельном входе ЦАП до тех пор, пока тактовый импульс не будет подан на регистры второго уровня — DACREGx. Разрешение этих регистров осуществляется установкой бита С1 в 1, а тактирование — подачей отрицательного импульса на вывод микросхемы. То есть мы можем загрузить один байт, скажем, в ЦАП А, а другой — в ЦАП В. Содержимое же обоих регистров DACREGx обновляется одновременно, что приводит к одновременному изменению сигналов на выходах VoutA и VoutB (см. Программу 12.5). Это можно сделать даже в то время, когда МАХ549А находится в режиме пониженного потребления, поскольку при переходе в данный режим содержимое регистров не изменяется. Из всего сказанного становится понятно, что каждая транзакция между микроконтроллером и микросхемой ЦАП состоит из пересылки двух 8-битных значений , сопровождающихся подачей нарастающего фронта на вывод .
Для примера, перешлем содержимое регистра h’20’ в ЦАП А, а содержимое регистра h’21’ — в ЦАП В. Затем перегрузим переданные значения в регистры ЦАП, в результате чего на выводах VoutA и VoutB одновременно появятся напряжения, эквивалентные содержимому регистров h’20’ и h’21’ соответственно.
В общей сложности для выполнения указанной задачи нам придется переслать четыре байта:
1. Управляющий байт 1: Ь’ХХХ00Х01’
Рабочий режим, обновить канал А, не формировать аналоговый сигнал.
2. Байт данных 1:
Содержимое регистра h’20’.
3. Подаем импульс на вывод .
4. Управляющий байт 2: Ь’ХХХ01X10’
Рабочий режим, обновить канал В, формировать аналоговый сигнал на обоих каналах.
5. Байт данных 2:
Содержимое регистра h’21’.
6. Подаем импульс на вывод .
Код, осуществляющий указанные операции, приведен в Программе 12.5. Для передачи каждого из четырех байтов используется подпрограмма SPI_WRITE, после передачи каждой пары байтов формируется импульс на выводе . В управляющем байте второй пары бит С1 устанавливается в 1, в результате чего одновременно с загрузкой входного регистра канала В оба байта данных перегружаются в регистры ЦАП.
Программа 12.5. Работа с двухканальным ЦАП МАХ549
СЕ equ 2
; ******************
; * ФУНКЦИЯ: Загружает новые данные в каналы А и В ЦАП МАХ549А *
; * и осуществляет одновременное обновление выходов *
; * РЕСУРСЫ: Подпрограмма SPI_WRITE *
; * ВХОД: Значение канала А — регистр h’20’, канала В — h’21’ *
; * ВЫХОД: Изменяется состояние обоих аналоговых выходов *
; ******************
MAX549A movlw b’00000001’ ; 1-й управляющий байт
movwf DATA_OUT ; Помещаем в требуемый регистр
call SPI_WRITE ; и пересылаем в МАХ549А
movf CHANNEL_A,W ; Берем значение канала А
movwf DATA_OUT ; Помещаем в требуемый регистр
call SPI_WRITE ; и пересылаем в МАХ549А
bsf PORTA,CE ; Формируем импульс на СЕ
bcf PORTA,CE
movlw b’00001010’ ; 2-й управляющий байт
movwf DATA_OUT ; Помещаем в требуемый регистр
call SPI_WRITE ; и пересылаем в МАХ549А
movf CHANNEL_B,w ; Берем значение канала В
movwf DATA_OUT ; Помещаем в требуемый регистр
call SPI_WRITE ; и пересылаем в МАХ549А
bsf PORTA,CE ; Формируем импульс на СЕ
bcf PORTA,CE
return
Если мы снимем осциллограммы с трех выводов микросхемы МАХ549А, то увидим сигналы, похожие на те, что изображены на Рис. 12.6 (на рисунке показана передача 1-й пары байтов . Во время передачи на выводе удерживается НИЗКИЙ уровень, а данные побитно загружаются во внутренний сдвиговый регистр. После 2-го байта, т. е. после 16-го тактового импульса, подача на вывод напряжения ВЫСОКОГО уровня активирует регистры ЦАП, заданные в управляющем байте.
Из Рис. 12.6 видно, что изменение состояния линии DIN, управляемой выводом SDO микроконтроллера, происходит перед формированием активного нарастающего фронта на выводе SCK. Очевидно, что состояние должно измениться за определенное время до появления фронта и удерживаться в течение короткого интервала времени после него. В документации на МАХ549А сказано, что минимальное время установки fDS составляет 30 нс, а время удержания fDH — 10 нс. Наша схема будет удовлетворять этим требованиям в любом случае, поскольку даже при тактовой частоте 20 МГц длительность машинного цикла микроконтроллера PIC будет равна 200 нс.
Рис. 12.6. Передача данных в МАХ549А по шине SPI
Благодаря наличию у микросхем входа СЕ к линиям SCK/SDO можно подключить несколько ЦАП — последовательные данные будут заноситься только в ту микросхему, на выводе которой будет присутствовать НИЗКИЙ уровень. На Рис. 12.7 изображены две микросхемы МАХ549А, подключенные к одной шине SPI и формирующие 4 аналоговых выхода. А, подключив к выводам RA[3:2] дешифратор 2–4, мы сможем управлять четырьмя МАХ549А, используя для этого всего четыре вывода порта.
Рис. 12.7. Подключение нескольких МАХ549А к одной шине SPI
Большинство микроконтроллеров среднего уровня и все микроконтроллеры старшего уровня имеют в своем составе модуль синхронного последовательного порта (SSP), который реализует, помимо всего прочего, протокол SPI. В зависимости от функциональной насыщенности конкретной модели микроконтроллера существует три очень похожих исполнения этого модуля. Первое из этих исполнений называется базовым SSP (BSSP), из которого позже «вырос» обычный SSP. В самых последних моделях появился модуль MSSP (ведущий синхронный последовательный порт). В этом модуле вводится несколько дополнительных опций формирования тактового сигнала SPI, однако гораздо большее значение имеет тот факт, что данный модуль может использоваться в качестве ведущего шины I2С (отсюда и название).
Несколько упрощенная структурная схема модуля MSSP, сконфигурированного для работы по протоколу SPI, приведена на Рис. 12.8. Основным узлом модуля MSSP является регистр специального назначения SSPBUF, расположенный по адресу h’13’. Байт данных, загруженный в этот РСН, автоматически перегружается в сдвиговый регистр модуля SSPSR и побитно выдается на вывод RC5/SDO микроконтроллера. Одновременно с этим восемь битов данных считываются с вывода RC4/SDI. После завершения указанных операций принятый байт автоматически пересылается в регистр SSPBUF, откуда его можно считать. Это индицируется установкой флага BF (Buffer Full) в регистре состояния SSPSTAT, формат которого приведен на Рис. 12.9. После чтения регистра SSPBUF флаг BF автоматически сбрасывается.
Рис. 12.8. Модуль MSSP, сконфгурированный для работы по протоколу SPI. Выводы модуля задействуют линии параллельного порта С
В отличие от параллельных портов ввода/вывода, конфигурирование и контроль состояния интерфейсных модулей микроконтроллеров, как правило, осуществляются с помощью ряда соответствующих регистров управления и состояния. Помимо этого используются биты масок и флагов прерываний, расположенные либо в регистре INTCON, либо в одном или двух регистрах разрешения прерываний от периферийных устройств и регистрах флагов прерываний от периферийных устройств, аналогичных показанным на Рис. 7.6 (стр. 224). Конфигурирование регистров управления, состояния и прерываний периферийных устройств обычно выполняется в той же части программы, в которой производится конфигурирование параллельных портов ввода/вывода. Так как интерфейсные модули в обязательном порядке задействуют выводы параллельных портов, то эти выводы часто необходимо конфигурировать даже в том случае, если порты ввода/вывода в программе не используются. Конфигурация линий ввода/вывода переопределяется автоматически при включении периферийного модуля или же должна явно задаваться самим программистом. К сожалению, ответ на этот вопрос далеко не всегда очевиден, поэтому для получения точной информации необходимо обращаться к документации на конкретный микроконтроллер.
Но вернемся к нашему модулю MSSP. На Рис. 12.9 показаны установки регистров SSPCON и SSPSTAT, используемые при работе в режиме SPI. Ну, а для подключения к внешним устройствам в этом режиме в общей сложности задействуется четыре вывода.
∙ RC5/SDO
Бит TRISC[5]См., например: Рональд Дж. Точчи, Нил С.Уидмер . Цифровые системы. Теория и практика: 8-е изд.: Пер. с англ. — М.: Издательский дом «Вильямс», 2004.
должен быть сброшен для переключения этого вывода в режим выхода.
∙ RC4/SDI
Этот бит конфигурируется модулем MSSP как вход независимо от состояния соответствующего бита TRISC[4]Здесь имеется в виду не размер данных, которыми оперирует микроконтроллер, а число битов, использующихся для записи слова команды. — Примеч. пер.
,
∙ RC3/SCK
При работе в качестве ведущего бит TRSC[3]New Scientist , vol.59, no. 2141, 4 July 1998, p.139.
New Scientist , vol.59, no. 2141, 4 July 1998, p.139.
должен быть сброшен, поскольку на этом выводе формируется тактовый сигнал. И, наоборот, при работе в режиме ведомого бит TRISC[3]New Scientist , vol.59, no. 2141, 4 July 1998, p.139.
New Scientist , vol.59, no. 2141, 4 July 1998, p.139.
должен быть установлен, чтобы принимать тактовый сигнал от ведущего.
∙
В режиме ведомого Ь’0100’ этот вывод должен быть сконфигурирован как вход (TRISA[5]См., например: Рональд Дж. Точчи, Нил С.Уидмер . Цифровые системы. Теория и практика: 8-е изд.: Пер. с англ. — М.: Издательский дом «Вильямс», 2004.
= 1), чтобы другой ведущий мог выбрать данное устройство.
При любом сбросе оба регистра SSPCON и SSPSTAT очищаются, а внутренний счетчик битов обнуляется. При этом модуль отключен, и если программист собирается использовать MSSP, то он должен задать соответствующие значения различных управляющих битов.
∙ SSPEN
Установка бита SSPCON[5]См., например: Рональд Дж. Точчи, Нил С.Уидмер . Цифровые системы. Теория и практика: 8-е изд.: Пер. с англ. — М.: Издательский дом «Вильямс», 2004.
в 1 разрешает работу модуля последовательного синхронного порта. Если модуль выключен, то соответствующие выводы могут использоваться в качестве линий-обычных параллельных портов ввода/вывода.
∙ SSPM[3:0]
Четыре бита выбора режима работы модуля, расположенные в SSPCON[3:0], используются для выбора протокола обмена, а также различных опций работы ведущего/ведомого. На Рис. 12.9 указано шесть комбинаций этих битов, относящихся к протоколу SPI.
Рис. 12.9. Регистры управления (SSPCON) и состояния (SSPSTAT) модуля MSSP при его работе в режиме SPI
Режимы ведущего (а их четыре) отличаются друг от друга только значением частоты тактового сигнала и ее источниками. В трех из этих режимов тактовый сигнал формируется из тактового сигнала микроконтроллера. Например, при использовании резонатора с частотой 20 МГц частота сигналов на выводе SCK может быть равна 5 МГц, 1.25 МГц и 312.5 кГц (период 200 не, 800 не и 3.2 мкc соответственно). В последнем же из режимов частота тактового сигнала шины SPI равна половине частоты сигнала, формируемого при переполнении Таймера 2 (см. Рис. 13.8 на стр. 474). Этот режим используется, когда требуется очень низкая скорость передачи данных.
При работе модуля в качестве ведомого тактовый сигнал поступает на вывод SCK извне от внешнего ведущего устройства. Кроме того, ведущий может управлять выводом SS для выбора одного из нескольких ведомых (см. Рис. 12.12).
∙ SSPOV
При работе модуля в режиме ведомого единица в этом бите указывает на то, что новый байт был принят раньше, чем считан предыдущий, т. е. то, что произошла потеря одного или нескольких байтов. Этот бит не сбрасывается автоматически при чтении регистра SSPBUF и должен быть сброшен самостоятельно, из программы. В режиме ведущего данный бит не используется.
∙ WCOL
Если программа попытается записать данные в регистр SSPBUF до завершения передачи предыдущего байта, то операция записи будет прервана, а бит конфликта записи WCOL установится в 1. В случае обнаружения такой ситуации необходимо сбросить этот бит программно и повторить операцию записи.
∙ СКР, СКЕ, SMP
Эти три бита предназначены для задания моментов формирования фронтов тактового сигнала, по которым будет осуществляться считывание битов с линии данных и выставление их на линию.
Чтобы проиллюстрировать использование указанных битов, рассмотрим ситуацию, когда модуль MSSP работает в режиме ведущего. Как ведущий, модуль полностью управляет тактовым сигналом на линии SCK, который используется для тактирования сдвигового регистра как передатчика, так и приемника. Для ведомого-приемника активный фронт тактового сигнала должен формироваться в тот момент, когда значение бита данных, выставленного ведущим на вывод SDO, станет стабильным. А сдвиговый регистр ведомого-передатчика должен тактироваться таким образом, чтобы выставляемые им значения битов данных были стабильны в то время, когда ведущий считывает их с вывода SDI. Такая схема изображена на Рис. 12.12, где ведущее устройство на базе микроконтроллера PIC может взаимодействовать с одним из двух ведомых устройств, каждое из которых может одновременно принимать и передавать данные. Этими ведомыми устройствами могут быть другие микроконтроллеры PIC (как показано на рисунке) или любые SPI-совместимые устройства.
Процесс передачи каждого байта состоит из восьми фаз, как показано на Рис. 12.11. В любом случае очередной бит данных Dn выставляется на вывод SDO вскоре (как правило, не позже чем через 50 нc) после начала соответствующей фазы тактового сигнала (см. верхнюю часть рисунка). Удаленный ведомый-приемник должен «защелкнуть» это значение в середине фазы. Подобным образом удаленный ведомый-передатчик должен своевременно выдавать на вывод SDI ведущего очередной бит своих данных dn, чтобы ведущий мог его считать.
На Рис. 12.10 изображены две часто встречающиеся ситуации. Диаграммы сигнала SCK в верхней части рисунка соответствуют случаям, когда передатчики и приемники используют различные активные фронты тактового сигнала. Поскольку тактирование передатчика осуществляется в начале каждой фазы, его данные необходимо считывать в середине фазы, для чего бит SMP должен быть установлен в 1.
Рис. 12.10. Прием и передача данных удаленными ведомыми устройствами
∙ СКЕ: СКР = 0:0
Когда тактирование удаленного передатчика осуществляется по нарастающему фронту сигнала SCK, ведущий должен считывать его данные с вывода SDI в середине фазы. Эти данные должны присутствовать на выводе как минимум за 100 не до наступления указанного момента и удерживаться в течение, как минимум, 100 не после. Удаленный ведомый-приемник считывает переданные ему данные с вывода SDO по спадающему фронту сигнала SCK также в середине фазы. В спецификации протокола SPI такой режим работы называется режимом 0,1 (или просто режим 1).
∙ СКЕ: СКР = 0:1
Режим 1,1 (режим 3) похож на предыдущий, за исключением того, что тактирование передатчика осуществляется по спадающему фронту , а приемника — по нарастающему .
Диаграммы в нижней части Рис. 12.10 соответствуют ситуации, когда тактирование сдвиговых регистров удаленных передатчиков и приемников осуществляется по одному и тому же активному фронту. Поскольку передатчик выдает данные в середине фазы, то данные от него, присутствующие на выводе SDI, должны считываться в конце фазы, для чего бит SMP должен быть сброшен в 0.
∙ СКЕ: СКР = 1:0
Режим 0,0 (режим 0) используется тогда, когда тактирование ведомых-передатчиков и ведомых-приемников производится одновременно в середине фазы по нарастающему фронту . К этому моменту значение бита данных от ведущего Dn станет стабильным, что позволит ведомому устройству считать его. Соответственно, данные от ведомого устройства должны быть готовы для считывания к концу фазы.
∙ СКЕ: СКР = 1:1
В режиме 1,0 (режим 2) тактирование ведомых-передатчиков и ведомых-приемников осуществляется в середине фазы по спадающему фронту тактового сигнала.
Когда модуль MSSP работает в качестве ведомого, тактовый сигнал на него поступает от внешнего устройства. Как и прежде, любые данные, предварительно загруженные в регистр SSPBUF, будут в начале каждой фазы тактового сигнала выставляться на вывод SDO. При этом биты СКЕ и СКР все равно необходимо устанавливать в зависимости от того, по какому фронту тактового сигнала удаленный передатчик выставляет свои данные и какой фронт для удаленного приемника является активным. Установки указанных битов зависят также от того, когда ведущий выставляет первый бит своих данных Dn — до первого тактового импульса или после. В любом случае модуль MSSP, сконфигурированный как ведомый, должен считывать значения этих битов со своего вывода SDI в конце каждой фазы, т. е. бит SMP должен быть сброшен в 0. Чтобы посмотреть подробные временные диаграммы, советую вам обратиться к фирменной документации на используемый микроконтроллер.
Когда микроконтроллер PIC работает в качестве ведомого SPI-устройства, то для его выбора удаленным ведущим используется вывод выбора ведомого . При появлении на выводе напряжения ВЫСОКОГО уровня, даже в середине транзакции, внутренний счетчик битов сбрасывается в 0. Кроме того, вывод SDO переключается в режим выхода с открытым стоком, чтобы дать возможность другому устройству захватить линию.
∙ BF, SSPIF
После считывания микроконтроллером полного фрейма из восьми битов и пересылки его в буферный регистр SSPBUF бит BF устанавливается в 1, извещая о приеме нового байта. При этом также устанавливается флаг прерывания SSPIF в регистре PIR1 (см. Рис. 7.6 на стр. 224), и, если установлен соответствующий бит маски прерывания SSPIE в регистре PIE1, генерируется прерывание. Если модуль MSSP работает в качестве ведомого, а микроконтроллер находится в «спящем» режиме, то данное прерывание можно использовать для «пробуждения» микроконтроллера. Это возможно благодаря тому, что тактовые импульсы на выводе SCK формируются внешним ведущим устройством, и поэтому микроконтроллеру не обязательно находиться в активном режиме, т. е. системный генератор может быть выключен.
При считывании нового байта из регистра SSPBUF бит BF автоматически сбрасывается. Если же новое значение своевременно не считать, то принятый байт будет потерян и, сигнализируя об этом, будет установлен флаг SSPOV. Флаг прерывания SSPIF необходимо сбрасывать самостоятельно в процедуре обработки прерывания.
Вооружившись информацией, приведенной на Рис. 12.8 и Рис. 12.9, мы теперь легко можем перечислить операции, которые необходимо выполнить для осуществления передачи байта и/или приема нового байта:
1. Сконфигурировать модуль SSP;
• Сделать SCK, RC5/SDO выходами, a RC4/SDI и, при необходимости, RA5/ — входами.
• Выбрать режим работы модуля (ведущий/ведомый) и источник тактового сигнала.
• Задать активные фронты с помощью битов СКР, СКЕ и SMP.
• Разрешить работу модуля SSP установкой бита SSPEN.
2. Загрузить байт в регистр SSPBUF для инициирования передачи.
3. Если бит WCOL = 1, то сбросить его и перейти к п. 2.
4. Ждать установки бита BF.
5. Скопировать полученный байт данных из SSPBUF, при этом бит BF будет сброшен автоматически.
Для иллюстрации описанного процесса рассмотрим подпрограмму SPI_IN_OUT, которая объединяет в себе функции SPI_READ и SPI_WRITE, т. е. передает содержимое регистра DATA_OUT и возвращает полученный байт в регистре DATA_IN. Предполагается, что сдвиговые регистры удаленного устройства «защелкиваются» по нарастающему фронту, т. е. используется режим SPI 0,0.
Реализация этой подпрограммы зависит от установок модуля MSSP, заданных во время инициализации основной программы. В следующем фрагменте кода мы переводим модуль MSSP в режим ведущего и задаем тактовую частоту шины SPI равной fOSC/4:
include "p16f877.inc’
MAIN bsf STATUS,RP0 ; Переключаемся в 1-й банк
movlw b’11010111’ ; RC5/SD0 и RC3/SCK — выходы
movwf TRISC ; RC4/SDI — вход
movwf b’11000000’ ; Устанавливаем биты SMP и СКЕ
movwf SSPSTAT;
... ...
bcf STATUS,RP0 ; Возвращаемся в 0-й банк
movlw b’00100000’ ; Включаем SSP, пассивный уровень — НИЗКИЙ
movwf SSPCON ; Режим ведущего SPI, Fosc/4
Код, приведенный в Программе 12.6, в точности соответствует вышеприведенному списку операций. Байт данных, который необходимо передать, копируется из указанного регистра в регистр SSPBUF, после чего проверяется бит состояния WCOL, чтобы удостовериться, что новое значение действительно было загружено в буфер. Если в этот момент осуществлялась передача предыдущего байта, то новый байт не будет загружен в регистр SSPBUF, а бит WCOL будет установлен в 1. Если обращение к модулю SSP осуществляется только из указанной подпрограммы, то возникновение такой ситуации маловероятно, и в большинстве случаев эта проверка может быть исключена. Тем не менее наличие такой проверки увеличивает надежность системы.
Программа 12.6. Использование модуля SSP для приема и передачи данных по шине SPI
;************************
; * ФУНКЦИЯ: Передает и одновременно принимает один байт *
; * ФУНКЦИЯ: данных по шине SPI с использованием модуля SSP *
; * ВХОД: Передаваемый байт — в DATA_OUT *
; * ВЫХОД: Принятый байт — в DATA_IN *
;************************
SPI_IN_OUT
movf DATA_OUT,w ; Берем байт для передачи
movwf SSPBUF ; Загружаем его в SSPBUF
SSP_IN_OUT_LOOP
btfss SSPCON,WCOL ; Он загрузился?
goto SPI_IN_OUT_CON7 ; ЕСЛИ да, TO продолжим
bcf SSPCON,WCOL ; ИНАЧЕ сбросим WCOL и
goto SSP_IN_OUT_LOOP ; попытаемся снова
SPI_IN_OUT_CONT
bsf STATUS,RPO ; Переключаемся в 1-й банк
btfss SSPSTAT,BF ; Проверяем состояние буфера
goto SPl_IN_OUT_CONT ; ЕСЛИ не полон, проверяем снова
bcf STATUS,RPO ; Возвращаемся в 0-й банк
movf SSPBUF,w ; ИНАЧЕ считываем принятый байт
movwf DATA_IN ; и помещаем его в требуемый POН
return
После загрузки передаваемого байта в буфер сразу же начинается процесс передачи, изображенный на Рис. 12.11. По окончании передачи устанавливается флаг BF, и принятый байт может быть скопирован из регистра SSPBUF в требуемый РОН. Флаг BF при этом сбросится автоматически.
Рис. 12.11. Временные диаграммы, соответствующие работе модуля SSP в режиме ведущего SPI
Помимо небольшого уменьшения размера кода, преимуществом использования аппаратного модуля является увеличение скорости передачи. Одна транзакция приема/передачи состоит из восьми тактов SCK, которые в нашем случае становятся равными восьми машинным циклам. При fOSC = 20 МГц частота сигнала SCK равна 5 МГц (т. е. скорость передачи составляет 5 миллионов битов в секунду; обычно это записывается как 5 Мбит/с). Таким образом, для передачи одного бита требуется всего 1.6 мкс.
На Рис. 12.11 показаны временные диаграммы работы модуля в режиме SPI,который используется в нашей подпрограмме. Поскольку мы сбросили бит СКР и установили бит СКЕ, то в режиме ожидания на линии SCK будет присутствовать НИЗКИЙ уровень. Сразу же после загрузки байта в регистр SSPBUF на вывод SDO выдается старший бит передаваемого байта. Это значение будет занесено в сдвиговый регистр удаленного приемника по нарастающему фронту тактового сигнала в середине фазы.
Поскольку удаленный приемник также тактируется в середине фазы, у него имеется достаточно времени для выдачи очередного бита данных на вход SDI микроконтроллера. Этот бит считывается микроконтроллером в конце каждой фазы.
* * *
Одним из применений последовательной передачи данных является объединение нескольких устройств в одну многопроцессорную систему. Например, каждый сустав манипулятора робота может управляться своим микроконтроллером, обменивающимся данными с основным процессором. Простая многоабонентская система из одного ведущего и двух ведомых процессоров показана на Рис. 12.12.
Рис. 12.12. Многоабонентская сеть на базе шины SPI
В этой схеме ведущий PIC-микроконтроллер управляет выводами SCK обоих ведомых, определяя, таким образом, периодичность и скорость обмена данными по сети. Оба ведомых устройства работают в режиме Ь’0100’, в котором разрешена работа входов SS. Таким образом, если ведущий собирается прочитать данные из ведомого № 2, то он подает на вход SS последнего НИЗКИЙ уровень и считывает восемь битов из регистров SSPBUF/SSPSR 2-го ведомого в свои регистры SSPBUF/SSPSR. Одновременно с этим ведомый принимает любые данные, передаваемые ведущим.
Си-процедуры для работы с шиной SPI могут быть написаны либо по аналогии с ассемблерными процедурами (выполняя установку/чтение соответствующих регистров специального назначения), либо с использованием специальных встроенных функций компилятора. Основными функциями компилятора CCS, управляющими модулем SSP в режиме SPI, являются:
setup_spi(spi_master I spi_h_to_1 I spi_clk_div_4);
Функция setup_spi с указанными параметрами переводит модуль SSP в режим ведущего SPI с активным нарастающим фронтом сигнала и частотой шины SPI, равной 1/4 частоты основного тактового генератора. Эти константы, как и многие другие, скажем spi_slave, spi_sample_at_end и spi_xmit_1_to_h, определены в стандартных заголовочных файлах, таких как 16f877a.h. Данная функция также конфигурирует соответствующие выводы портов А и С.
∙ spi_write(value);
Эта функция используется для передачи байта по шине SPI. Возврат из функции осуществляется после установки флага BF.
∙ spi_read();
Эта функция практически идентична функции spi_write (), за исключением того, что она возвращает значение байта, принятого модулем SSP. Если в данную функцию будет передано значение, то оно будет передано по шине.
∙ spi_data_is_in();
Эта функция возвращает ненулевое значение, если по шине SPI были получены данные, т. е. если флаг BF установлен.
Чтобы проиллюстрировать использование указанных функций, напишем процедуру для взаимодействия с микросхемой МАХ549А (см. Программу 12.5). Прежде всего, нам необходимо сконфигурировать модуль SSP. Это можно сделать следующим образом:
#include <16f877a.h>
#bit СЕ = 5.2 /* 2-й бит порта А — сигнал СЕ для МАХ549А */
void МАХ549А (unsigned int channel_A, unsigned int channel_B);
void main(void)
{
set_tris_a(0xFB); /* CE = RA2 — выход */
setup_adc(NO_ANALOGS); /* Все входы портов А и E — цифровые */
setup_spi (spi_master I spi_1_to_h| spi_clk_div_4);
В приведенном выше фрагменте предполагается, что вывод СЕ микросхемы МАХ549А подключен к выводу RA2 порта А, как показано на Рис. 12.7.
В подпрограмме (см. Программу 12.7) четыре раза вызывается функция spi_write (), причем после передачи каждой пары значений на вывод микросхемы подается положительный импульс.
Программа 12.7. Управление ЦАП МАХ549А на Си
void МАХ549А(unsigned int channel_A, unsigned int channel_B)
{
spi_write(0x01); /* Передаем 1-й управляющий байт */
spi_write(channel_A); /* Передаем байт данных */
CE=0; /* Формируем импульс */
CE=1;
spi_write(0х0А); /* Передаем 2-й управляющий байт */
spi_write(channel_B); /* Передаем байт данных */
CE=0; /* Формируем импульс */
CE=1;
}
Несмотря на то что протокол SPI достаточно быстрый, для его реализации требуется как минимум три линии плюс по одной линии для выбора каждого ведомого устройства. Даже если не принимать во внимание ценовой фактор, добавление в законченную схему нового устройства потребует модификации аппаратной части изделия. Однако, увеличив степень «интеллектуальности» ведомых устройств, мы сможем использовать один-единственный последовательный поток для передачи управляющей информации, адреса и данных. Именно эта концепция легла в основу протокола I2C™ (Inter-Integrated Circuit — протокол межсоединения интегральных схем), разработанного компанией Philips/Signetics Corporation в начале 1980-х годов. В интерфейсе I2С количество линий связи уменьшено до двух за счет использования двунаправленной передачи данных (см. Рис. 12.13).
∙ SCL
Это линия тактового сигнала, используемая для синхронизации передачи данных и выполняющая те же функции, что и линия SCK в протоколе SPI. Однако линия SCL является двунаправленной — это позволяет различным ведущим устройствам захватывать управление ею в разные моменты времени.
Изначально в спецификации I2С максимальная скорость передачи была ограничена на уровне 100 Кбит/с (частота сигнала SCL — 100 кГц), однако в 1993 году спецификация была дополнена высокоскоростным режимом Fast, обеспечивающим скорость передачи до 400 Кбит/с, который в настоящее время стал стандартом де-факто. В 1998 году был введен режим High-Speed, обеспечивающий скорость передачи до 3.4 Мбит/с.
∙ SDA
Это линия данных, позволяющая передавать данные в обоих направлениях — как от ведущего к ведомому (ведущий-передатчик), так и от ведомого к ведущему (ведущий-приемник). Более того, наличие двунаправленной линии позволяет приемнику отсылать свое состояние передатчику после передачи каждого байта.
Рис. 12.13. Передача данных по шине I 2 С
Протокол I2С достаточно сложен, и полную его спецификацию можно найти на сайте компании Philips Corporation. Прежде чем перейти к изучению основ этого протокола, рассмотрим подробнее работу линий SDA и SCL. В отсутствие передаваемых данных на обеих линиях присутствует ВЫСОКИЙ уровень (состояние ожидания). Устройство, собирающееся захватить управление шиной, находящейся в состоянии ожидания, должно выставить на вывод SDA НИЗКИЙ уровень. Такое состояние шины называется состоянием СТАРТ. Чтобы любое устройство, желающее стать ведущим, могло выставить на эту линию НИЗКИЙ уровень, выводы SDA всех остальных устройств, подключенных к шине, должны быть «отключены» от линии, а сама линия шины должна быть подтянута внешним резистором до напряжения ВЫСОКОГО уровня (см. Рис. 12.14, а). Для этого выходы SDA (а также SCL) всех I2С-совместимых устройств выполняются по схеме с открытым коллектором или открытым стоком (см. Рис. 2.2, б на стр. 32). Это означает, что любое устройство, подключенное к шине, может перевести ее линию в состояние НИЗКОГО уровня, выставив на свой выход лог. 0.
Помимо формирования состояния СТАРТ, ведущий должен генерировать тактовый сигнал, а также передавать значение адреса другим устройствам на шине для установления соединения с одним или более ведущими устройствами. Один из битов в байте адреса используется для того, чтобы сообщить ведомому, какая посылка ожидается далее: от ведущего к ведомому (ведущий-передатчик) или от ведомого к ведущему (ведущий-приемник).
Каждый пакет, передаваемый по шине, состоит из девяти битов. Восемь из них представляют собой байт данных, синхронизируемый тактовым сигналом. Состояние линии SDA должно изменяться только при НИЗКОМ уровне на линии SCL. Данные считываются приемником по следующему спадающему фронту сигнала SCL. Эти байты могут являться адресом или управляющей информацией, посылаемой ведущим, или же данными, передаваемыми как ведущим, так и ведомым. В протоколе I2С предусмотрен механизм квитирования (см. Рис. 11.2 на стр. 329). Во время девятого тактового импульса передающее устройство высвобождает линию SDA, и принимающее устройство подтверждает (acknowledges) данные, посланные передатчиком. Если данные были успешно приняты, то линия SDA удерживается приемником в состоянии НИЗКОГО уровня — это состояние называется АСК (подтверждение) (см. Рис. 12.15). Если же при приеме возникли некие проблемы или приемник не хочет больше принимать данные, то он оставляет на линии ВЫСОКИЙ уровень — такое состояние называется NACK (нет подтверждения). В последнем случае передатчик, как правило, пытается повторить передачу еще несколько раз, прежде чем принять решение о завершении обмена.
В качестве менее радикального способа ведомое устройство может удерживать линию тактового сигнала в состоянии НИЗКОГО уровня. Растягивание тактового сигнала (clock stretching) полезно в тех случаях, когда ведомое устройство не может обрабатывать поступающие данные с требуемой скоростью. После высвобождения ведомым линии SCL ведущий продолжит формирование тактовых импульсов.
В любом случае только ведущий может прекратить обмен, выставляя на линии SDA ВЫСОКИЙ уровень при ВЫСОКОМ уровне на линии SCL; такое состояние называется состоянием СТОП. При необходимости ведущий может начать обмен с другим ведомым, повторно сформировав на шине состояние СТАРТ. Кроме того, ведущий может формировать состояние СТАРТ, не генерируя перед этим состояние СТОП; в таком случае это состояние называется повторный СТАРТ или просто ПОВСТАРТ. К примеру, ведущий собирается передать (ведущий-передатчик) внутренний адрес ячейки в I2С-совместимую микросхему памяти (см. Пример 12.3), а затем прочитать (ведущий-приемник) данные из этой ячейки. Для выполнения этой операции потребуется сменить направление передачи данных, что осуществляется повторным формированием состояния СТАРТ и посылкой нового адресного пакета с требуемым значением бита направления (см. Рис. 12.27). Различие между использованием состояния ПОВСТАРТ и СТОП заключается в том, что последнее сигнализирует другим устройствам на шине, что ведущий освободил шину и что другое устройство может стать ведущим.
При программной реализации интерфейса I2С в микроконтроллерах PIC возникает проблема, заключающаяся в том, что выходы портов не являются выходами с открытым стоком, т. е. состояние лог. 1 не формируется разомкнутой цепью, как того требует спецификация (см. Рис. 12.14, а). Однако состояние с высоким входным сопротивлением можно эмулировать путем переключения линии порта с выхода на вход. Так, если мы хотим использовать для управления линией SCL вывод RA2, то для формирования на этой линии отрицательного импульса мы должны сделать так:
bcf PORTA,2 ; При конфигурировании RA2
... ...
STATUS,RP0 ; Переключаемся в 1-й банк
bcf TRISA,2; На выходе RA2 — лог. 0
nор ; Короткая задержка
bsf TRISA,2 ; Переводим RA2 в третье состояние, делая его входом
bcf STATUS,RP0 ; Возвращаемся в 0-й банк
При этом ВЫСОКИЙ уровень на линии формируется совместным влиянием внешнего подтягивающего резистора и высокого входного сопротивления, как показано на Рис. 12.14, б (справа).
Рис. 12.14. Совместное использование линий SCL и SDA несколькими устройствами
Собственно процесс обмена между ведущим и ведомым устройствами состоит из передачи различных пакетов (один пакет — 8 бит данных плюс бит квитирования), передаваемых между состояниями СТАРТ и СТОП. Содержимое этих пакетов может слегка изменяться в зависимости от требований ведомого устройства, однако последовательность передачи пакетов всегда остается неизменной (см. Рис. 12.15): сначала передается адрес ведомого, затем — управляющий байт или команда, а потом передается один или несколько байтов данных.
Рис. 12.15. Порядок передачи пакетов по шине I 2 С
В соответствии со спецификацией I2С каждое ведомое устройство должно иметь уникальный адрес. Этот адрес выдается производителю I2С-совместимых микросхем и заносится в них на этапе изготовления. Чтобы на одной шине можно было использовать несколько однотипных микросхем, большинство I2С-совместимых устройств позволяют разработчику задавать до 4 битов адреса самостоятельно, обычно путем подачи на выводы адреса соответствующих логических уровней. После обнаружения состояния СТАРТ все ведомые устройства на шине проверяют первые семь битов на совпадение со своим адресом. Если совпадения не обнаружено, то до появления нового состояния СТАРТ это устройство игнорирует все изменения на шине. Восьмой бит пакета адреса (R/W¯) определяет направление передачи данных. Этот бит равен 0, если ведущее устройство намеревается передавать данные, т. е. писать (Write) в ведомое устройство, и 1 — если ведущий собирается считывать (Read) данные из ведомого устройства.
Не все 7-битные адреса являются допустимыми. В частности, все адреса формата Ь’0000ХХХ’ или b’1111ХХХ’ зарезервированы для специальных применений; таким образом, в распоряжении пользователей остается 224 возможных адреса.
Например, адрес Ь’0000000’ обозначает широковещательный пакет, посылаемый всем ведомым на шине, а не какому-то конкретному устройству. Одновременно с введением режима Fast в протоколе I2С появилась возможность использования 10-битных адресов. На использование расширенной адресации указывает передача зарезервированного адреса Ь’111110ХХХ’, три младших бита которого будут объединены с содержимым последующего 7-битного пакета адреса.
Байт, передаваемый вслед за байтом (или байтами) адреса, обычно интерпретируется адресованным ведомым как команда или управляющий/байт, посредством которого передается конфигурационная информация. Например, для I2C-совместимых микросхем памяти может потребоваться передача внутреннего адреса, начиная с которого будут заноситься данные (см. Пример 12.3). Все последующие байты обычно являются простыми данными или же данными, перемежающимися управляющими байтами.
Для иллюстрации рассмотрим микросхему ЦАП МАХ518 компании Maxim, блок-схема которой приведена на Рис. 12.16. Эта микросхема аналогична SPI-совместимой МАХ459, имеет двухуровневый регистровый конвейер, два канала и режим пониженного потребления.
Микросхема МАХ518 имеет 7-битный адрес вида 01011AD1AD0, где значения битов AD1 и ADO определяются логическими уровнями на соответствующих выводах микросхемы. Если предположить, что оба вывода подключены к общему проводу, то для адресации микросхемы ведущий должен будет послать следующий пакет: . Бит R/W¯ равен 0, поскольку эта микросхема может только принимать данные.
Байт команды имеет формат 000 RST PD XX А0 и содержит три управляющих бита.
∙ А0
Этот бит используется для разрешения входного PIPO-регистра 0-го (А0 = 0) и 1-го (А0 = 1) каналов.
∙ PD
Когда этот бит равен 1, оба канала АЦП переключаются в режим пониженного потребления, при этом суммарный ток потребления микросхемы не превышает 4 мкА. Содержимое внутренних регистров остается неизменным, кроме того, в этом состоянии можно загружать данные и обновлять содержимое регистров. Изменение режима работы происходит только после формирования ведущим состояния СТОП, т. е. учитывается только последнее переданное значение бита.
∙ RST
Если этот бит равен 1, то все внутренние регистры сбрасываются независимо от значения последующего байта данных. После формирования на шине состояния СТОП напряжение на выходах обоих каналов становится равным нулю.
В любом случае изменение сигнала на аналоговом выходе в соответствии со значением управляющего байта и байта данных происходит только после формирования на шине состояния СТОП. Если с момента формирования последнего состояния СТОП было послано несколько пар байтов, то на состояние устройства окажут влияние только последние значения.
Рис. 12.16. Микросхема сдвоенного ЦАП с интерфейсом I 2 C МАХ518 (Maxim)
Для работы с ЦАП МАХ518 нам потребуется написать подпрограмму, формирующую состояния СТАРТ, СТОП и передающую по шине байты данных. Для написания подобного драйвера устройства необходимо более внимательно рассмотреть временные соотношения между тактовым сигналом и сигналом данных, на которые в большинстве своем накладываются более жесткие ограничения, чем в случае интерфейса SPI.
Микросхема МАХ518 и большинство современных I2С-совместимых устройств поддерживают режим Fast, поэтому диаграммы, приведенные на Рис. 12.17, соответствуют тактовой частоте для этого режима (400 кГц). В частности, для корректного формирования состояния СТАРТ требуется, чтобы ВЫСОКИЙ уровень на линии SCL удерживался в течение не менее 0.6 мкс (tHD;STA) после появления активного фронта на линии SDA. Аналогично, для корректного формирования состояния СТОП требуется, чтобы ВЫСОКИЙ уровень на линии SCL был выставлен не позже чем за 0.6 мкс (tSU;STO) до появления активного фронта на линии SDA. А время нахождения шины в незанятом состоянии (tBUF) между формированием состояния СТОП и последующего состояния СТАРТ должно быть не менее 1.3 мкс. Указанные значения позволяют всем ведомым устройствам однозначно обнаруживать эти специальные состояния шины.
Рис. 12.17. Минимальные значения временных параметров для режима Fast
Во время передачи байта данных тактовый сигнал должен удовлетворять следующим условиям: длительность интервала НИЗКОГО уровня (tLOW) — не менее 1.3 мкс, длительность интервала ВЫСОКОГО уровня (tHIGH) — не менее 0.6 мкс и, следовательно, полный период сигнала — не менее 2.5 мкс, что соответствует частоте 400 кГц. Изменение состояния линии данных может происходить только при НИЗКОМ уровне на линии тактового сигнала, причем все изменения должны быть прекращены не позже чем за 100 не (tSU;DAT) до появления нарастающего фронта тактового сигнала.
На Рис. 12.17 не указаны максимальные значения времени нарастания и спада, которые не должны превышать 300 не при наибольшей емкости шины, равной 400 пФ. Чтобы удовлетворить этому требованию, при такой емкости шины сопротивление подтягивающих резисторов, изображенных на Рис. 12.14, не должно превышать 1.8 кОм. При небольшой длине шины и малом количестве подключенных к ней устройств сопротивление подтягивающих резисторов можно увеличить в десятки раз для уменьшения потребления.
Если мы воспользуемся микроконтроллером PIC, работающим на частоте более 3.2 МГц (время исполнения команды менее 1.25 мкс), то для формирования временных интервалов, удовлетворяющих спецификации I2С, может потребоваться вставка коротких задержек между отдельными операциями. Например, если мы используем резонатор с частотой 20 МГц, то при выполнении следующих строк:
bcf TRISA,SCL
; Выставить на линию НИЗКИЙ уровень, записав в порт 0
bsf TRISA,SCL
; Сформировать на линии ВЫСОКИЙ уровень, переключив вывод на вход
длительности интервалов ВЫСОКОГО и НИЗКОГО уровней тактового сигнала будут составлять всего 0.2 мкс. Обычно короткие задержки формируются командами nop, каждая из которых выполняется за один машинный цикл (fOSC/4). Соответственно, для формирования тактового сигнала частотой 400 кГц при 20-МГц резонаторе можно написать:
bcf PORTA,SCL ; Выставляем НИЗКИЙ уровень
nop ; 0.2 мкс
nop ; 0.4 мкс
nop ; 0.6 мкс
nop ; 0.8 мкс
nop ; 1.0 мкс
nop ; 1.2 мкс
bsf PORTA,SCL ; Выставляем ВЫСОКИЙ уровень
nop ; 1.6 мкс
nop ; 1.8 мкс
nop ; 2.0 мкс
nop ; 2.2 мкс
nop ; 2.4 мкс
nop ; 2.6 мкс
Разумеется, меньшее значение тактовой частоты потребует меньше операций nop. Вместо того чтобы корректировать наши подпрограммы в соответствии с используемым в каждом конкретном случае резонатором, мы воспользуемся макрокомандой Delay_600, код которой приведен в Программе 12.8. Эта макрокоманда вставляет в программу столько операций nop, сколько требуется для формирования задержки длительностью 600 нc (0.6 мкс), в зависимости от значения константы XTAL, заданной программистом. Например, чтобы запустить Программу 12.9 на микроконтроллере с 12-МГц резонатором, необходимо просто заменить строку
#define XTAL 20
на
#define XTAL 12
и перекомпилировать программу.
Программа 12.8. Макрокоманда формирования короткой задержки, независимой от частоты резонатора
Delay_600 macro ; Формирует задержку длительностью 0.6 мкс
if (XTAL <= 6)
nop ; Одна команда пор, если частота резонатора < 6 МГц
endif
if ((XTAL > 6) && (XTAL <= 13))
nop ; Две команды nор, если частота резонатора
nop ; от 6 до 13 МГц
endif
if (XTAL > 13)
nop ; Три команды пор, если частота резонатора
nop ; выше 13 КГц
nop
endif
endm
В Программе 12.8 используются ассемблерные директивы условной компиляции if — endif. Директива if похожа на условный оператор языка Си (см. стр. 293) тем, что вставляет в программу все команды, расположенные между ней и последующей директивой endif, если аргумент директивы if имеет значение ИСТИНА. Например, выражение if ((XTAL>6&& (XTAL<=13)) означает, что если значение константы больше 6 и меньше или равно 13, то в программу будет вставлено две команды пор. При частоте 13 МГц время их выполнения будет равно примерно 600 нc. На практике различные команды, управляющие состоянием линий шины и выполняющие вспомогательные задачи, будут вносить дополнительные задержки, поэтому если необходимо достичь максимальной скорости передачи, то длительности задержек придется подбирать более точно.
Используя макрокоманду из Программы 12.8 и учитывая приведенный ниже инициализационный код (в котором из соображений удобства обращение к регистру направления порта А осуществляется с использованием косвенной адресации):
include "p16f877a.inc"
#define XTAL 20
SCL equ 0
SDA equ 1
MAIN movlw h’85’ ; Инициализируем регистр FSR,
movwf FSR ; чтобы он указывал на TRISA (регистр h’85’)
bcf PORTA,SCL ; Сбрасываем биты порта в 0, чтобы впоследствии
bcf PORTA,SDA ; можно было заставлять на линии НИЗКИЙ уровень
bsf INDF,0 ; Формируем на линии тактового сигнала (TRISA[0])
bsf INDF,1 ; и линии данных (TRISA[1]) ВЫСОКИЙ уровень
мы можем написать три подпрограммы для работы с I2С-совместимой микросхемой МАХ518 (предполагается, что под линии SCL и SDA задействованы 0-й и 1-й выводы порта А микроконтроллера PIC16F84, работающего на частоте 20 МГц). Код этих подпрограмм приведен в Программе 12.9.
∙ START
Эта подпрограмма сначала высвобождает линии SCL и SDA, на которых в результате формируется ВЫСОКИЙ уровень на время не менее 1.3 мкс (fBUF). Затем путем выдачи на вывод SDA НИЗКОГО уровня на шине формируется состояние СТАРТ, после чего формируется задержка длительностью 0.6 мкс для выдерживания интервала tHD;STA (см. Рис. 12.17). После возврата из подпрограммы на обеих линиях будет присутствовать НИЗКИЙ уровень.
∙ STOP
Для формирования состояния СТОП на обе линии сначала выставляется НИЗКИЙ уровень (в принципе шина и так должна находиться в таком состоянии после передачи сброшенного бита квитирования). Затем высвобождается линия SCL, в результате чего на ней появляется ВЫСОКИЙ уровень. А после задержки длительностью 0.6 мкс (tSU;STO) высвобождается линия SDA, формируя тем самым состояние СТОП. После возврата из подпрограммы на обеих линиях шины будет присутствовать ВЫСОКИЙ уровень, т. е. шина будет находиться в состоянии ожидания, готовая к формированию следующего состояния СТАРТ.
∙ I 2 C_OUT
Эта подпрограмма передает по шине восемь битов содержимого регистра DATA_OUT, начиная со старшего бита, и контролирует подтверждение приема ведомым.
Первая операция реализуется путем многократного сдвига содержимого регистра с проверкой флага переноса, значение которого выдается на линию SDA. После выдачи каждого бита данных на линии SCL формируется тактовый импульс, параметры которого tLOW и tHIGH удовлетворяют значениям, указанным на Рис. 12.17.
Программа 12.9. Подпрограммы низкого уровня для управления шиной I 2 С
; **************
; * ФУНКЦИЯ: Формирует на дине состояние СТАРТ *
; * ВХОД: FSR указывает на регистр TRIS порта, *
; * подключенного к шине I2C *
; * ВЫХОД: Формируется состояние СТАРТ, SCL и SDA — НИЗКИЙ уровень *
; **************
START bsf INDF,SDA ; Гарантируем, что перед состоянием СТАРТ
bsf INDF,SCL ; линии тактового сигнала и данных находились
Delay_600 ; в режиме ожидания (ВЫСОКИЙ уровень)
Delay_600 ; в течение не менее 1.3 мкс
bcf INDF,SDA ; Формируем спадающий фронт на линии данных
Delay_600 ; Ждем, чтобы ведомый мог его обнаружить
bcf INDF,SCL ; Выходим, при этом на SCL — НИЗКИЙ уровень
return
; ****************
; * ФУНКЦИЯ: Формирует на дине состояние СТОП *
; * ВХОД: FSR указывает на регистр TRIS порта, подключенного к шине I2C *
; * ВЫХОД: Формируется состояние СТОП, SCL и SDA — ВЫСОКИЙ уровень *
; ****************
STOP bcf INDF,SCL ; Гарантируем наличие НИЗКОГО уровня на
bcf INDF,SDA ; линии тактового сигнала и данных
bsf INDF, SCL ; Формируем на линии тактового сигнала
Delay_600 ; ВЫСОКИЙ уровень на время не менее 0.6 мкс
bsf INDF,SDA ; Формируем нарастающий фронт на линии данных
return
; ***************
; * ФУНКЦИЯ: Передает байт ведомому и проверяет подтверждение *
; * ВХОД: 8 бит передаваемых данных в DATA_OUT *
; * РЕСУРСЫ: Подпрограммы START и STOP *
; * ВЫХОД: Байт передан. Если не было подтверждения, ERR = 1 *
; * ВЫХОД: ИНАЧЕ ERR = 00. SCL — НИЗКИЕ уровень *
; ***************
I2C_OUT bcf TNDF,SCL ; На линии такт, сигнала — НИЗКИЙ уровень
clrf ERR ; Сбрасываем признак ошибки
movlw 8 ; Инициализируем счетчик цикла
movwf COUNT
I2C_OUT_LOOP
bcf INDF,SDA ; Попробуем выдать 0 на линию данных
rlf DATA_OUT,f ; Сдвигаем исходный байт влево
btfsc STATUS,С ; С = 0 или 1?
bsf INDF,SDA ; ЕСЛИ последнее, TO выдаем на линию 1
Delay_600 ; Формируем требуемую задержку
Delay_600
bsf TNDF,SCL ; Выдаем на линию такт, сигнала ВЫСОКИЙ
Delay_600 ; уровень на время не менее 0.6 мкс
bcf INDF,SCL ; Выдаем ка линию такт, сигнала НИЗКИЙ уровень
decfsz COUNT,f ; Декрементируем счетчик цикла
goto I2C_OUT_LOOP ; и повторяем восемь раз
; Теперь проверим наличие подтверждения от ведомого
bsf INDF,SDA ; Высвобождаем линию данных
Delay_600 ; Сохраняем на линии такт, сигнала НИЗКИЙ уровень
Delay_600 ; на время, достаточное для ответа
bsf INDF,SCL ; Выдаем на линию такт, сигнала ВЫСОКИЙ уровень
bcf sc INDF, SDA ; Проверяем наличие НИЗКОГО уровня на линии данных
incf ERR,f ; ЕСЛИ нет, TO ERR = 1
bcf INDF,SCL ; Переводим линию такт, сигнала в состояние НИЗКОГО уровня
return
После выхода из цикла линия данных высвобождается, а на линии SCL в течение tLOW удерживается НИЗКИЙ уровень. Затем линия SCL высвобождается (на ней появляется ВЫСОКИЙ уровень) и проверяется состояние линии SDA, на которую ведомый должен был выставить НИЗКИЙ уровень. Если это не так, значит, подтверждения не было (NACK) и в регистр ERR заносится число h’01’; в противном случае в этом регистре возвращается 0.
Нашу программу нельзя считать полностью рабочей, поскольку в ней отсутствует обработка ошибок. А ошибки могут возникать, например, если какое-либо устройство будет удерживать на любой из линий НИЗКИЙ уровень, т. е. если шина будет занята.
Мы не стали реализовывать в подпрограмме I2C_OUT блок ведущего-приемника, поскольку в микросхеме МАХ518 не предусмотрена передача данных ведомому. Однако функция приема данных по шине 12С реализована в Программе 12.18 (подпрограмма I2C_IN).
В качестве примера перешлем содержимое регистра h’40’ в 0-й канал, а содержимое регистра h’41’ — в 1-й канал. После этого обновим оба регистра ЦАП и, следовательно, одновременно сформируем напряжения на выводах Vout1 и Vout2, эквивалентные содержимому регистров h’40’ и h’41’ соответственно. При этом предполагается, что оба вывода AD0 и AD1 подключены к общему проводу.
Для выполнения указанных операций нам потребуется переслать по шине пять пакетов:
1. СТАРТ.
2. Пакет адреса: Ь’01011000’.
Адрес ведомого Ь’01011(00), запись.
3. Управляющий байт 1: Ь’00000ХХ0’.
Нет сброса, активный режим, канал 0.
4. Байт данных 1:
Содержимое регистра h’40’.
5. Управляющий байт 2:
Нет сброса, активный режим, канал 1.
6. Байт данных 2:
Содержимое регистра h’41’.
7. СТОП; содержимое обоих регистров ЦАП обновляется.
Ход выполнения Программы 12.10 в точности повторяет указанную последовательность операций. После каждого возврата из подпрограммы I2C_OUT регистр ERR проверяется на нулевое значение. Если он не равен нулю, то последовательность повторяется с самого начала — повторное формирование состояний СТАРТ допускается протоколом I2С. Однако если произошел аппаратный сбой самой шины или ведомого устройства, то этот процесс может продолжаться бесконечно. Поэтому для увеличения надежности и предотвращения зависания системы необходимо предусмотреть механизм тайм-аута.
Программа 12.10 . Работа с I 2 С-совместимой микросхемой двух канального ЦАП МАХ518
ANALOG call START ; Начинаем передачу
; Байт адреса ---------------------
movlw b’01011000’ ; Адрес ведомого, режим — ведущий-передатчик
movwf DATA_OUT ; Копируем в промежуточный регистр
call I2C_OUT ; Передаем
movf ERR,f ; Проверяем ка наличие ошибок
btfsc STATUS,Z ; ЕСЛИ ноль, TO продолжаем
goto ANALOG ; ИНАЧЕ пробуем снова
; Управляющий байт 1 -----------
movlw b’00000000’ ; Нет сброса, активный режим, канал 1
movwf DATA_OUT ; Копируем в промежуточный регистр
call I2C_OUT ; Передаем
movf ERR, f ; Проверяем на наличие ошибок
btfsc STATUS,Z ; ЕСЛИ ноль, TO продолжаем
goto ANALOG ; ИНАЧЕ пробуем снова
; Байт данных 1
movf 20h,w ; Считываем значение канала 0 из памяти
movwf DATA_OUT ; Копируем в промежуточный регистр
call I2C_OUT ; Передаем
movf ERR,f ; Проверяем на наличие ошибок
btfsc STATUS,Z ; ЕСЛИ ноль, TO продолжаем
goto ANALOG ; ИНАЧЕ пробуем снова
; Управляющий байт 2
moviw b’00000001’ ; Нет сброса, активный режим, канал 1
movwf DATA_OUT ; Копируем в промежуточный регистр
call I2C_OUT ; Передаем
movf ERR,f ; Проверяем на наличие ошибок
btfsc STATUS,Z ; ЕСЛИ ноль, TO продолжаем
goto ANALOG ; ИНАЧЕ пробуем снова
; Байт данных 2
movf 21h,w ; Считываем значение канала 1 из памяти
movwf DATA_OUT ; Копируем в промежуточный регистр
call I2C_OUT ; Передаем
movf ERR,f ; Проверяем на наличие ошибок
bcfsc STATUS,Z ; ЕСЛИ ноль, TO продолжаем
goto ANALOG ; ИНАЧЕ пробуем скова
call STOP
Любые варианты модулей SSP микроконтроллеров PIC поддерживают работу в режиме I2С. Ранние версии модулей позволяли использовать микроконтроллер только в качестве ведомого устройства, тогда как модуль ведущего синхронного последовательного порта MSSP автоматически обеспечивает работу микроконтроллера в качестве ведущего устройства при наличии на шине нескольких ведущих, на что, собственно, и указывает его наименование. Спецификация шины I2С разрешает наличие нескольких ведущих устройств, но, разумеется, не одновременно. Предотвращение конфликтов на шине является довольно сложной задачей, поэтому использование модуля MSSP в качестве ведущего шины I2С в данной книге рассматриваться не будет. Использование модуля MSSP в этом качестве подробно описано в документе AN7578 «Use of the SSP Module in the I 2 C MultiMaster Environment ». Мы же ограничимся изучением работы модуля MSSP в качестве ведомого устройства I2С.
На Рис. 12.18 приведена структурная схема модуля SSP, сконфигурированного для работы в качестве ведомого I2С-устройства. Для подключения к двунаправленной линии данных SDA используется вывод RC4, а к линии SCL — вывод RC3. При работе модуля в режиме I2С оба вывода должны быть сконфигурированы как входы.
Собственно ввод и вывод данных производится посредством сдвигового регистра SSPSR, который используется и для приема данных, и для их передачи.
Рис. 12.18. Структурная схема модуля MSSP, сконфигурированного для работы в качестве ведомого I 2 С-устройства
Передача
При посылке ведомым устройством данных удаленному ведущему, который при этом находится в режиме ведущий-приемник, байт данных, помещенный в буферный регистр SSPBUF, автоматически пересылается в регистр SSPSR (если тот пуст), из которого затем побитно выдается на линию SDA. Если регистр SSPSR полон, то данные на линию не выдаются, и устанавливается флаг конфликта записи.
Прием
Если ведомое устройство ожидает пакет от удаленного ведущего, то данные побитно вдвигаются через вывод SDA и после приема всех восьми битов полученный байт пересылается в регистр SSPBUF. Если ошибки переполнения не было, то модуль MSSP автоматически формирует подтверждение (АСК) во время 9-го тактовою импульса. Эта ошибка возникает в том случае, если ранее принятый байт не был в свое время считан из регистра SSPBUF.
После обнаружения состояния СТАРТ все ведомые устройства на шине принимают первый пакет от ведущего и сравнивают его содержимое со значением, записанным в регистре адреса SSPADD. При совпадении старших семи битов (0-й бит — бит направления передачи) соответствующее устройство формирует подтверждение (АСК) и готовится к обмену данными с ведущим. При этом устанавливаются оба флага — BF и SSPIF, сигнализирующие о событии на шине. Как мы уже видели на Рис. 12.15, 8-й бит первого пакета адреса указывает ведомому устройству, принимать или передавать данные до появления на шине следующего состояния СТАРТ или СТОП.
Как и в SPI-режиме, для конфигурирования модуля MSSP нам необходимо записать определенные значения в регистры управления и состояния. Формат регистров, приведенный на Рис. 12.19, соответствует четырем возможным режимам работы модуля SSP в качестве ведомого устройства I2С. В этом режиме используются те же регистры SSPSTAT и SSPCON. К сожалению, названия некоторых битов, используемых в этом режиме, например СКЕ, остаются прежними, хотя их назначение кардинальным образом меняется. По сравнению с более старыми модулями SSP модуль MSSP имеет второй регистр управления SSPCON2. За исключением 0-го и 7-го битов, этот регистр используется исключительно при работе модуля в качестве ведущего I2С.
Рис. 12.19. Регистры управления и состояния модуля MSSP при его работе в режиме ведомого I 2 С
∙ SSPEN
Установка бита SSPCON[5]См., например: Рональд Дж. Точчи, Нил С.Уидмер . Цифровые системы. Теория и практика: 8-е изд.: Пер. с англ. — М.: Издательский дом «Вильямс», 2004.
разрешает работу синхронного последовательного порта. После любого сброса модуль MSSP отключен, а выводы RC3 и RC4 могут использоваться в качестве линий порта С.
∙ SSPM[3:0]
К нашему обсуждению относятся четыре комбинации этих битов выбора режима работы модуля SSP. Для простоты мы будем считать, что используется режим 7-битной адресации. При использовании 10-битной адресации сначала необходимо загружать в регистр SSPADD старший байт адреса b’1110А9А8’, а после его совпадения с принятым значением заменять на младший байт адреса Ь’А7А6А5А4А3А2А1’. Режимы Ь’0110’ и Ь’1110’ отличаются только тем, что в последнем при обнаружении состояний СТАРТ и СТОП устанавливается флаг прерывания SSP1F.
∙ BF, SSPIF
Установленный флаг BF свидетельствует о том, что с данными в регистре SSPBUF что-то произошло. Флаг SSPIF является флагом прерывания от модуля MSSP и устанавливается при любом событии на шине I2С.
Ведомый-приемник
При приеме кадра от ведущего и записи его содержимого в регистр SSPBUF устанавливается флаг BF, показывая тем самым, что новые данные доступны для обработки, а во время 9-го тактового импульса передается подтверждение (АСК). Также при этом устанавливается флаг SSPIF (PIR[3]New Scientist , vol.59, no. 2141, 4 July 1998, p.139.
), который может использоваться для генерации прерывания. При считывании полученного байта из буфера бит BF автоматически сбрасывается (этот флаг доступен только для чтения), однако флаг SSPIF необходимо сбрасывать вручную, как и остальные флаги прерываний.
В случае приема нового байта до считывания предыдущего значения, т. е. при установленном бите BF, он не пересылается в буфер SSPBUF. Вместо этого устанавливается флаг SSPOV, извещающий о возникновении переполнения. В этом случае подтверждение не посылается (NACK).
Ведомый-передатчик
В течение всего времени пересылки байта ведущему флаг BF остается установленным, показывая, что идет передача. Если в этот момент попытаться записать в регистр SSBUF новый байт, то вместо его пересылки в сдвиговый регистр SSPSR будет установлен флаг WCOL, извещающий о возникновении конфликта записи.
∙ SSPOV
При работе в режиме ведомый-приемник попытка считывания регистра SSPBUF до приема нового байта индицируется установкой данного флага. При этом ведущему передается NACK. Ведомый может намеренно передавать NACK, чтобы информировать ведущего о том, что тот может повторить попытку передачи позже. Это состояние сбрасывается при считывании регистра SSPBUF (при этом сбрасывается флаг BF) и ручном сбросе флага SSPOV.
∙ WCOL
Этот флаг устанавливается при попытке записи в регистр SSPBUF во время передачи, извещая о возникновении конфликта записи. Данный флаг должен быть сброшен вручную.
∙ S,P
Эти флаги индицируют обнаружение на шине состояний СТАРТ и СТОП соответственно. Обычно они имеют противоположные значения. Исключением из данного правила является их состояние после любого сброса микроконтроллера или при разрешении модуля (SSPEN — > 1) — в эти моменты оба флага сброшены.
Установленный флаг Р показывает, что шина свободна. Эта информация может потребоваться следящему за состоянием шины устройству, которое намеревается начать работать в качестве ведущего.
∙ D/A¯, R/W¯, UA
Эти флаги относятся к пакету (пакетам), передаваемым по шине после формирования состояния СТАРТ и содержащим информацию об адресе ведомого устройства и направлении передачи последующих пакетов.
Бит D/А¯ показывает, какой именно байт находится в регистре SSPBUF — данные (D) или адрес (А¯).
Бит R/W¯ информирует программу о том, в каком направлении будут передаваться последующие пакеты данных — к ведущему (R/W¯ = 1) или от ведущего (R/W¯= 0). Вообще говоря, значение данного бита соответствует значению 0-го бита (первого) пакета адреса.
Бит UA используется только в режимах с 10-битной адресацией. В этих режимах сначала сравниваются семь старших битов первого байта адреса b’11110A9A80’. Младший бит является битом направления передачи и информирует о том, что следующий пакет адреса будет передаваться ведущим (R/W¯ = 0). После приема 1-го байта адреса флаг UA автоматически устанавливается в 1, извещая программу о том, что в регистр SPPADD можно загружать младший байт адреса. После выполнения записи флаг UA автоматически сбрасывается.
∙ GCEN
Если бит разрешения общего вызова GCEN равен 1, то флаг прерывания SSPIF будет устанавливаться при приеме адреса общего вызова Ь’0000000’ независимо от значения, находящегося в регистре адреса модуля. Прием этого адреса свидетельствует о том, что ведущий собирается приступить к широковещательной рассылке всем ведомым устройствам. В более ранних вариантах модуля SSP данная функция отсутствует.
∙ CKP, SEN
При сброшенном бите СКР ведомое устройство удерживает линию SCL в состоянии НИЗКОГО уровня, запрещая, таким образом, ведущему генерацию тактовых импульсов. После установки бита СКР в 1, ведомый освобождает линию SCL, позволяя ведущему формировать тактовые импульсы для нового пакета. Хотя бит СКР может быть изменен программно в любой момент времени (т. е. вручную), растягивание тактового сигнала может выполняться автоматически.
SEN = 0
При сброшенном бите SEN (состояние по умолчанию), а также в модулях SSP более ранних версий бит СКР сбрасывается автоматически в конце каждого пакета, отсылаемого модулем ведущему. Ведомый должен устанавливать бит СКР каждый раз после загрузки содержимого нового пакета в регистр SSPBUF для высвобождения линии тактового сигнала и разрешения передачи следующего пакета. Растягивание тактового сигнала в таких ситуациях осуществляется всегда, независимо от состояния бита SEN. Точно так же работают модули, в которых этот бит отсутствует.
SEN = 1
В последних версиях модуля MSSP автоматическое растягивание тактового сигнала при установленном бите SEN разрешается как при передаче от ведомого устройства, так и при приеме. Последнее полезно в том случае, если ведущий посылает пакеты быстрее, чем ведомый успевает их обрабатывать.
∙ СКЕ
Этот бит имеется только в модулях MSSP, и при его установке электрические параметры сигналов на линиях SDA и SCL будут соответствовать спецификации шины SMBus.
Работа модуля MSSP в качестве ведомого представляет собой многоэтапный процесс, требующий от программы реакции на всевозможные события, возникающие на шине I2С. Хотя это можно реализовать с помощью простого опроса флага SSPIF регистра PIR1, в наших примерах мы будем использовать прерывания.
Прежде чем перейти к рассмотрению этих событий и знакомству с учебными программами, необходимо разобраться с инициализацией модуля MSSP. Типичный инициализационный код для микроконтроллера PIC16F877A, который должен работать в качестве ведомого с адресом h’06’, при частоте шины 100 кГц выглядит следующим образом:
include "p16f877a.inc"
SETUP movlw b’00110110’ ; Включаем MSSP, такт, линия свободна
movwf SSPCON ; Режим ведомого с 7-битной адресацией (0110)
bsf STATUS,RP0 ; переключаемся в 1-й банк
bsf SSPSTAT,SMP ; Скорость нарастания соответствует частоте 100 кГц
bsf SSPCON2,SEN ; SEN = 1 для автоматического удержания линии тактового сигнала после приема данных
movlw h’0C’ ; Адрес h’06’ сдвигаем влево на один бит,
movwf SSPADD ; чтобы значение соответствовало содержимому пакета
bsf PIE1,SSPIE ; Разрешаем прерывание от модуля SSP
bsf INTCON,PEIE ; Разрешаем прерывания от периферийных устройств
bsf INTCON,GIE ; Разрешаем прерывания
bcf STATUS,RP0 ; Возвращаемся в 0-й банк
В этом фрагменте:
1. В регистры управления и состояния модуля MSSP заносятся значения, соответствующие заданию.
2. Адрес ведомого (число h’06’, сдвинутое влево для соответствия семи старшим битам пакета адреса) заносится в регистр SSPAD.
3. Установкой бита маски SSPIE совместно с битами PEIE и GIE разрешается прерывание от модуля MSSP (см. Рис. 7.5 на стр. 223).
Теперь, когда у нас есть код для инициализации модуля MSSP и системы прерываний, можно приступать к написанию процедуры обработки прерывания, распознающей различные события, происходящие на шине I2С. Любое допустимое событие приведет к передаче управления из фоновой программы в обработчик прерывания. Даже если микроконтроллер будет находиться в «спящем» режиме, это событие вызовет установку бита SSPIF и «пробуждение» микроконтроллера.
Вот эти события:
1. Ведущий-передатчик: принятый пакет был пакетом адреса
S = 1 Последним состоянием на шине было состояние СТАРТ.
R/W¯ = 0 Ожидается передача данных от ведущего.
D/А¯ = 0 Это пакет адреса.
BF = 1 Буфер полон.
Для сброса флага BF необходимо прочитать регистр SSPBUF, даже если пакет адреса, присланный ведущим, будет проигнорирован. Если этого не сделать, следующий байт, посланный ведущим, вызовет переполнение буфера (SSPOV —> 1) и модуль отошлет NACK.
Если бит SEN установлен в 1, то бит СКР будет автоматически сброшен и на линию SCL будет выставлен НИЗКИЙ уровень. Когда это станет возможным, необходимо будет установить бит СКР в 1 для разрешения работы ведущего.
2. Ведущий-передатчик: принятый пакет был пакетом данных
После передачи адресного пакета ведущий посылает один или более пакетов данных. Чтобы избежать возникновения переполнения и гарантировать отсылку подтверждения АСК, ведомое устройство должно считывать каждый из этих пакетов. Также при установленном бите SEN необходимо манипулировать битом СКР, как и при обработке предыдущего события. Содержимое регистра STATUS для этого события отличается от предыдущего только значением бита D/A¯.
S = 1 Последним состоянием на шине было состояние СТАРТ.
R/W¯ = 0 Ожидается передача данных от ведущего.
D/A¯ = 1 Это пакет данных.
BF = 1 Буфер полон.
3. Ведущий-приемник: принятый пакет был пакетом адреса
Обмен на шине начинается с посылки ведущим пакета адреса с установленным битом R/W¯, извещающим ведомого, что от него ожидается передача пакетов данных ведущему. После распознавания ведомым своего адреса или адреса общего вызова при установленном бите GCEN, состояния битов регистра SSPSTAT будут следующими:
S = 1 Последним состоянием на шине было состояние СТАРТ.
R/W¯ = 1 Ожидается передача данных к ведущему.
D/А¯ = 0 Это пакет адреса.
BF = 0 Буфер свободен для передачи.
Заметьте, что в данной ситуации бит BF сброшен. Дело в том, что при работе модуля в режиме ведущего-приемника флаг BF используется для того, чтобы сообщать программе о готовности регистра SSPBUF к загрузке байта данных, посылаемого ведущему. Поэтому, в отличие от 1-го события, нам нет необходимости считывать содержимое регистра SSPBUF.
После распознавания адреса ведомый может послать первый байт данных ведущему, загружая его в регистр SSPBUF и устанавливая бит СКР для высвобождения вывода SCL. Расширение тактового сигнала производится автоматически при получении пакета от ведущего-приемника, независимо от состояния бита SEN.
4. ведущий-приемник: принятый пакет был пакетом данных
Это событие похоже на предыдущее, и бит СКР в этом случае действует аналогичным образом. Новый байт нельзя загружать в регистр SSPBUF до тех пор, пока не будет сброшен бит BF, в противном случае установится бит WCOL.
Значения битов регистра SSPSTAT идентичны значениям для предыдущего события, за исключением установленного бита D/А, показывающего, что последним принятым пакетом был пакет данных:
S = 1 Последним состоянием на шине было состояние СТАРТ.
R/W¯ = 1 Ожидается передача данных к ведущему.
D/A¯ = 1 Это пакет данных.
BF = 0 Буфер свободен для передачи.
5. Ведущий-приемник: ведущий послал NACK
Эта ситуация обычно возникает, когда ведущий по какой-либо причине не хочет больше принимать данные от ведомого. Сигнал NACK указывает на завершение обмена, и при его приеме ведомый модуль сбрасывает логику I2С.
S = 1 Последним событием на шине было состояние СТАРТ.
R/W¯ = 0 Бит R/W¯ сбрасывается ведомым.
D/A¯ = 1 Это пакет данных.
BF = 0 Буфер свободен для передачи.
Появление NACK определяется по сброшенному биту BF при нулевом бите R/W¯. Это конфликтное состояние, поскольку такое сочетание битов говорит о том, что был принят пакет данных от ведущего, но буфер при этом остался пустым!
В качестве примера давайте представим, что ведомый микроконтроллер PIC16F877A с адресом h’06’ контролирует восемь температурных датчиков, подключенных к входам встроенного модуля АЦП (см. главу 14). Если ведущий хочет считать значение одного из этих оцифрованных каналов, то он сначала посылает ведомому номер канала (ведущий-передатчик) N, а затем, перейдя в режим ведущего-приемника, принимает от ведомого запрошенные данные.
Предположим, что подпрограмма GET_ANALOG (Программа 14.1, стр. 516) уже написана.
Последовательность операций может быть следующей:
1. Ведущий формирует на шине состояние СТАРТ, после чего адресует ведомого с адресом h’06’, приказывая ему принять следующий пакет данных (ведущий-передатчик).
2. Ведущий посылает пакет данных, содержащий номер канала 0…7.
3. Ведущий формирует состояние ПОВТСТАРТ для ведомого с адресом h’06’, требуя на этот раз, чтобы последний передал ему следующий пакет данных (ведущий-приемник).
4. Ведомый удерживает линию тактового сигнала, дожидаясь завершения преобразования по выбранному каналу.
5. Ведомый посылает требуемые значения.
6. Ведущий отвечает NACK, сообщая о завершении обмена.
Для ясности разобьем нашу программу на две отдельные процедуры. Кроме того, предположим, что для сохранения контекста используются ячейки, отображенные на все банки памяти. В случае микроконтроллера PIC16F877A эти ячейки располагаются по адресам h’60’…h’7F’. Переменные, используемые в программе, располагаются в 0-м банке.
В Программе 12.11 приведен код процедуры обработки прерывания, сохранение и восстановление контекста в которой осуществляются в соответствии с принципами, обсуждавшимися ранее (см. стр. 216). Перед восстановлением контекста бит СКР устанавливается в 1 для разрешения формирования тактового сигнала, а флаг SSPIF сбрасывается.
Программа 12.11. Процедура обработки прерывания I 2 С-совместимого регистратора температуры
; ***********
; * ФУНКЦИЯ: Обработчик для передачи значения N-ro канала по шине I 2 С *
; * ВХОД: Произошло событие на шине *
; * ОКРУЖЕНИЕ: Использует п/п GET_ANALOG, I2C_HANDLER *
; ***********
; Сначала сохраним контекст ------------
ISR movwf _work ; Сохраняем W
swapf STATUS,w ; и регистр STATUS
movwf _status
; Проверяем, установлен ли флаг SSPIF? —
bcf STATUS,RP0 ; Переключаемся ка 0-й банк
bcf STATUS,RP1
btfss PIR1,SSPIF ; Это прерывание от MSSP?
goto ISR_EXIT ; ЕСЛИ нет, TO выходим
bsf STATUS,RP1 ; ИНАЧЕ переключаемся на 1-й банк
movf SSPSTAT,w ; Считываем состояние из SSPSTAT
bcf STATUS,RP1 ; и возвращаемся в 0-й банк
andlw b’00101101’ ; Обнуляем все биты, кроме S, D/A, R/W и BF,
movwf I2C_STATUS ; и копируем полученное значение во временный регистр
clrf I2C_ERROR ; Обнуляем признак ошибки
call I2C_HANDLER ; Теперь обработаем событие на шине I2C
; Восстановим контекст —
ISR_SXIT bcf PIR1,SSPIF ; Сбрасываем флаг прерывания
bsf SSPCON,CKP ; Освобождаем линию такт. сигнала
swapf _status,w ; Восстанавливаем исходное значение STATUS
swapf _work,f ; Восстанавливаем исходное значение W,
swapf _work,w ; не изменяя битов регистра STATUS,
retfie ; и возвращаемся в фоновую программу
Собственно, обработчик просто проверяет состояние флага SSPIF и, если тот не установлен, процедура завершается. В реальной жизни прерывания могут генерироваться несколькими источниками, что повлечет за собой переделку этого фрагмента программы для проверки флагов всех используемых прерываний.
Если флаг SSPIF установлен, то содержимое регистра состояния SSPSTAT копируется из 1-го банка в регистр I2C_STATUS, расположенный в более удобном 0-м банке (перед этим сбрасываются не интересующие нас биты). Регистр I2C_ERROR также обнуляется. При обнаружении ошибочной ситуации в этот регистр будет занесено ненулевое значение для информирования фоновой программы.
После инициализации управление передается в подпрограмму, которая, собственно, и анализирует события, происходящие на шине I2С. Код данной подпрограммы приведен в Программе 12.12. Эта программа состоит из пяти блоков, каждый из которых соответствует одному из состояний шины I2С, перечисленных на стр. 412. Выбор требуемого блока осуществляется с использованием команды xorlw (см. стр. 146), с помощью которой проверяется равенство копии регистра SSPSTAT и константы, соответствующей тому или иному состоянию. При обнаружении равенства выполняются действия в соответствии с нашим алгоритмом или же просто вспомогательные операции, которые позволят модулю MSSP продолжить работу с корректного состояния. Так, нам ничего не нужно делать при возникновении 5-го состояния, при котором ведущий отсылает ведомому NACK, поскольку модуль MSSP будет сброшен автоматически. Если соответствий не обнаружено, декрементируется переменная I2C_ERROR, сигнализируя об ошибке.
Программа 12.12. Обработчик событий шины I 2 С регистратора температуры
; ************
; * ФУНКЦИЯ: Анализирует события вины I 2 С и реагирует требуемым образом *
; * ВХОД: Копия SSPCON в I2C_STATUS *
; * ВЫХОД: Выполняются требуемые действия I2C_ERROR = -1, ЕСЛИ событие не было распознано *
; ************
I2C_HANDLER ; 1-е событие? (пакет адреса, ведущий-передатчик) --------
movf I2C_STATUS,w ; Берем копию содержимого SSPSTAT
xorlw b’00001001’ ; Проверяем наличие S=1, D/A=0, R/W=0, BF=1
btfss STATUS,Z ; Равно?
goto STATE2 ; ЕСЛИ нет, TO проверяем 2-е событие
movf SSPBUF,w ; ИНАЧЕ читаем буфер для сброса флага BF
; 2-е событие? (пакет данных, ведущий-передатчик) ---------
STATE2 movf I2C_STATUS,w ; Берем копию содержимого SSPSTAT
xorlw b’00101001’ ; Проверяем наличие S=1, D/A=1, R/W=0, BF=1
btfss STATUS,Z ; Равно?
goto STATE3 ; ЕСЛИ нет, TO проверяем 3-е событие
movf SSPBUF,w ; ИНАЧЕ считываем номер канала
call GET_ANALOG ; Оцифровываем сигнал N-го канала
movwf TEMP ; и сохраняем результат в регистре ТЕMР
; 3-е событие? (пакет данных, ведущий-передатчик) ----------
STATE3 movf I2C_STATUS,w ; Берем копию содержимого SSPSTAT
xorlw b’00001100’ ; Проверяем наличие S=1, D/A=0, R/W=1, BF=0
btfss STATUS,Z ; Равно?
goto STATE4 ; ЕСЛИ нет, ТО проверяем 4-е событие
movf TEMP,w ; ИНАЧЕ берем значение температуры
movwf SSPBUF ; и помещаем в буферный регистр для передачи
; 4-е событие? (пакет данных, ведущий-передатчик) -----------
STATE4 movf I2C_STATUS,w ; Берем копию содержимого SSPSTAT
xorlw b’00101100’ ; Проверяем наличие S=1, D/A=1, R/W=1, BF=0
btfss STATUS,Z ; Равно?
goto STATES ; ЕСЛИ нет, TO проверяем 5-е событие
; Ничего не делаем!!!
; 5-е событие? (ведущий отослал ведомому NACK) ------------
STATE5 movf I2C_STATUS,w ; Берем копию содержимого SSPSTAT
xorlw b’00101000’ ; Проверяем наличие S=1, D/A=1, R/W=0, BF=0
btfss STATUS,Z ; Равно?
decf I2C_ERROR,f ; ЕСЛИ нет, TO сообщаем об ошибке
return
Еще один пример использования модуля SSP приведен в документе AN734 «Using the PIC Microcontroller SSP for Slave I 2 C Communications ».
Как и в случае с протоколом SPI, большинство Си-компиляторов для микроконтроллеров PIC имеют встроенные функции для реализации протокола I2С, что позволяет отказаться от написания собственных функций, манипулирующих различными битами регистров специального назначения.
В качестве примера рассмотрим Программу 12.13, написанную для компилятора CCS и выполняющую действия, аналогичные ассемблерной программе, фрагменты которой были приведены в Программах 12.9 и 12.10.
Программа 12.13. Взаимодействие с ЦАП MAX518 на Си
#include <16F84.h>
/* 0-й бит порта А — SCL, 1-й бит порта А — SDA, режим ведущего, протокол Fast */
#use i2c(master, scl=PIN_A0, sda=PIN_A1, fast)
#byte DATA_X = 0x20
#byte DATA_Y = 0x21
void MAX518 (unsigned int channel_0, unsigned int channel_1);
void irain(void)
{
/* Различный код * /
МАХ518(DATA_X, DATA_Y); /* Передаем два байта данных * /
/* Остальной ход * /
}
void MAX518(unsigned int channel_0, unsigned int channel_1)
{
i2c_start(); /* Формируем состояние СТАРТ * /
i2c_write(0x58); /* Передаем адрес ведомого * /
i2c_write(0); /* Посылаем 1-й управляющий байт * /
i2c_write(channel_0); /* Посылаем данные 0-го канала * /
i2c_write(0x01); /* Посылаем 2-й управляющий байт * /
i2c_write(channel_1); /* Посылаем данные 1-го канала * /
/* Обновляем оба канала * /
i2c_stop(); /* Формируем состояние СТОП * /
}
В этой программе используются следующие встроенные функции компилятора CCS:
∙ i2c_start();
Формирует состояние СТАРТ.
∙ i2c_stop();
Формирует состояние СТОП.
∙ i2c_read();
Считывает один байт с шины. Если необязательный параметр равен 0, то в ответ на принятые данные будет возвращен NACK. При работе в режиме ведущего также генерирует тактовый сигнал.
∙ i2c_write(value);
Передает по шине один байт. При работе в режиме ведущего также генерирует тактовый сигнал.
∙ #use i2c(master, scl=PIN_A0, sda=PIN_A1, fast)
С помощью этой директивы программист сообщает компилятору о том, какие выводы будут использоваться для подключения к линиям шины I2С, тип используемого протокола (стандартный или высокоскоростной), а также режим работы модуля (ведущий или ведомый). К моменту написания книги компилятор не поддерживал возможности модуля MSSP по работе в качестве ведущего, поэтому такие функции реализованы программно. Функции ведомого могут быть реализованы аппаратно модулем MSSP, если в директиве #use i2c() указать опцию FORCE_HW.
* * *
Отличительной особенностью всех последовательных протоколов, рассмотренных нами до настоящего момента, является то, что ведущее устройство генерирует тактовый сигнал, благодаря которому ведомое устройство может принимать и передавать данные в синхронном режиме. Альтернативный подход к передаче данных базируется на допущении, что передатчик и приемник работают примерно с одной и той же скоростью. Такой асинхронный протокол использовался в системах передачи данных на протяжении более ста лет для передачи алфавитно-цифровых символов по телеграфу, телефону и радиоканалу.
Одной из особенностей ранних компьютеров, создававшихся в 40-х и 50-х годах, было интенсивное заимствование существовавших к тому времени технологий. А одним из основных элементов любой машинно-ориентированной системы является терминал данных. В то время в области телекоммуникаций широко использовались телетайпы (TTY). Последовательные данные преобразовывались в параллельный формат самим терминалом, который также выполнял функции клавиатуры и печатающего устройства.
До начала 80-х годов телетайпы были исключительно электромеханическими устройствами, управляющимися синхронными электродвигателями. То есть синхронность работы удаленных терминалов гарантировалась только в течение короткого интервала времени. Для устранения этой проблемы каждому передаваемому слову предшествовал старт-бит, а после него передавался один или более стоп-битов. Типичный пример показан на Рис. 12.20. В свободном состоянии линии на ней присутствует лог. 1 (обрыв). Появление сигнала лог. 0 сигнализирует о начале слова. Завершает передачу слова сигнал лог. 1. Электромеханические терминалы обычно печатали со скоростью десять символов в секунду и требовали не менее двух стоп-битов. Для 8-битных слов данных это соответствует скорости передачи, равной 110 бит в секунду или 110 бод.
Рис. 12.20. Передача строки сообщения «РIС» по асинхронному последовательному каналу с проверкой четности и, как минимум, одним стоп-битом
Первый полностью электронный терминал требовал наличия всего одного стоп-бита и мог печатать со скоростью 300 символов в секунду, обеспечивая скорость передачи 300 бод. По традиции, для каналов передачи данных используются скорости, кратные 300, например 1200, 2400, 4800, 9600 и т. д. Последовательный порт ПК может работать на скоростях до 115 200 бод. Однако придерживаться этих значений, кратных 300, вовсе не обязательно — главное, чтобы приемник и передатчик работали с одинаковой номинальной скоростью.
Обычно приемник при обнаружении входящих данных пытается прочитать значение каждого бита примерно в середине интервала его передачи. То есть на интервале передачи 10 бит будет допустимым уход частоты в пределах ±0.5 бита. Соответственно частоты приемника и передатчика должны отличаться друг от друга не более чем на ±5 %, а их ресинхронизация будет производиться в начале каждого слова данных.
Несмотря на не самую большую эффективность, описанный асинхронный протокол имеет огромное преимущество, заключающееся в том, что он является международным стандартом. Существует несколько его вариантов, к примеру, размер слова может варьироваться от 5 до 9 бит. В нашем примере длина слова равна 8 бит, причем восьмой бит используется для контроля ошибок. Значения случае малых скоростей в тело цикла может потребоваться добавить дополнительные команды nop.
При использовании этой макрокоманды формирования задержки базовые подпрограммы ввода/вывода, код которых приведен в Программе 12.14, похожи на аналогичные подпрограммы для протокола SP1. Подпрограмма PUTCHAR просто выдает на вывод ТХ сигнал НИЗКОГО уровня в течение двух периодов Baud_delay, а затем изменяет состояние вывода восемь раз в соответствии с содержимым регистра DATA_OUT, начиная с младшего бита, т. е. в обратном порядке по сравнению с протоколами SPI/I2C. В конце на вывод ТХ выдается сигнал ВЫСОКОГО уровня для формирования сигнала СТОП.
Программа 12.14. Подпрограммы асинхронного приема и передачи данных
; **************
; * ФУНКЦИЯ: Передает 8-битное значение в асинхронном режиме *
; * ФУНКЦИЯ: Скорость передачи: 1200…9600 для XTAL 1…20 МГц *
; * РЕСУРСЫ: Макрокоманда BAUD_DELAY, формирующая задержку 0.5 битового интервала; COUNT *
; * ВХОД: 8-битное слово данных в DATA_OUT, предопределенные константы XTAL и BAUD *
; * ВЫХОД: Содержимое DATA_OUT обнуляется, байт передан *
; **************
PUTCHAR movlw 8 ; Восемь битов данных
movwf COUNT
bcf PORTA,ТХ ; Старт-бит
Baud_delay ; Задержка 2x0.5 бита
Baud_delay
; Теперь выдвигаем байт данных, начиная с младшего бита
PUTCHAR_LOOP rrf DATA_OUT,f ; Сдвигаем вправо через перенос
btfss STATUS,С ; Проверяем флаг переноса
goto ITS_A_0 ; ЕСЛИ 0, ТО передаем 0
bsf PORTA,TX ; ИНАЧЕ передаем 1
goto PUTCHAR_NEXT ; и продолжаем
ITS_A_0 bcf PORTA,TX ; Выдаем 0
PUTCHAR_NEXT
Baud_delay ; Задержка на 1 бит
Baud_delay
decfsz COUNT,f ; Повторяем восемь раз
goto PUTCHAR_LOOP
bsf PORTA,ТХ; Стоп-бит
Baud_delay
Baud_delay
return
; **************
; * ФУНКЦИЯ: Принимает 8-битное значение в асинхронном режиме *
; * : Скорость передачи: 1200…9600 для XTAL 1…20 МГц *
; * РЕСУРСЫ: Макрокоманда BAUD_DELAY, формирующая задержку 0.5 битового интервала; COUNT *
; * ВЫХОД: Принят байт в DATA_IN *
; * ВЫХОД: Если нет ошибки кадрирования, ERR = 0, ИНАЧЕ ERR = -1*
; **************
GETCHAR movlw 8 ; Восемь битов данных
movwf COUNT
clrf ERR ; Обнуляем байт признака ошибки
GETC HAR_START
btfsc PORTA,RX ; Ожидаем появления 0
goto GETCHAR_START
Baud_delay ; Ждем в течение 0.5 бита
btfsc PORTA,RX ; Все еще 0?
goto GETCHAR_START
Baud_delay ; ЕСЛИ да, TO ждем в течение 1 бита
Baud_delay
GETCHAR_LOOP bcf STATUS,С ;Сбрасываем флаг переноса
rrf DATA_IN,f ; Вдвиг аем 0 в байт данных
btfsc PORTA,RX ; На входе ВЫСОКИЙ уровень?
bsf DATA_IN,7 ; ЕСЛИ да, ТО устанавливаем бит
Baud_delay
Baud_delay
decfsz COUNT,f ; Повторяем восемь раз
goto GETCHAR_LOOP
btfss PORTA,RX; Проверяем приход стоп-бита (1)
decf ERR,f; ЕСЛИ 0 , ТО сообщаем об ошибке
return
Подпрограмма приема GETCHAR более сложна. Появление на выводе RX НИЗКОГО уровня расценивается как приход старт-бита. Однако если осуществлять выборку значений последующего потока данных с периодичностью, равной длительности битового интервала (два включения макроса Baud_delay), то, поскольку этот момент может соответствовать моменту окончания битового интервала, уход любой из двух частот может привести к появлению ошибок. Чтобы избежать этого, состояние вывода RX считывается повторно после задержки, равной половине битового интервала, чтобы еще раз убедиться в наличии старт-бита. Если это окажется так, то последующие выборки осуществляются с периодом, равным двум битовым интервалам. При этом моменты выборок будут приходиться примерно на середину интервала. Лучших результатов можно достичь, считывая состояние вывода с большей частотой (передискретизация) и принимая мажоритарное решение на основании считанных значений.
После приема восьми битов данных стоп-бит проверяется на равенство лог. 1. Если стоп-бит равен 0, значит, произошла ошибка кадрирования. Эта ситуация сигнализируется возвратом —1 в ERR. При использовании других, более развитых схем могут возвращаться сообщения об ошибках разных типов. Например, при использовании контроля четности может быть возвращена ошибка четности.
В качестве примера рассмотрим фрагмент кода, осуществляющий передачу 3-символьного сообщения «РIС». К счастью, ассемблер предоставляет программисту возможность вместо ASCII-кодов символов записывать сами символы в одинарных кавычках, как описано на стр. 267.
; Передадим строку "PIC"
movlw ’P’ ; Аналогично movlw h’50’ (ASCII-код символа «Р»)
movwf DATA_OUT ; Помещаем в память данных
call PUTCHAR ; Передаем
movlw ’I’ ; Аналогично movlw h’49’ (ASCII-код символа «I»)
movwf DATA_OUT ; Помещаем в память данных
call PUTCHAR ; Передаем
movlw ’C’ ; Аналогично movlw h’43’ (ASCII-код символа «С»)
movwf DATA_OUT ; Помещаем в память данных
call PUTCHAR ; Передаем
На самом деле такая реализация асинхронного обмена по последовательному каналу годится только в самых простых случаях. Например, если не отслеживать непрерывно состояние вывода RX, можно пропустить передачу или потерять синхронизацию. Кроме того, при таком подходе трудно реализовать дуплексную связь, не говоря уже о том, что большая часть вычислительной мощности микроконтроллера в данном случае тратится на выполнение циклов задержки. Эту ситуацию можно несколько улучшить, используя для формирования задержек внутренний таймер и работая по прерываниям. Однако в большинстве микроконтроллеров PIC, выпускающихся в «многовыводных» корпусах (более 28 контактов), для реализации асинхронного обмена данными имеется встроенный коммуникационный порт.
Одним из первых применений новых технологий производства БИС, появившихся в конце 60-х годов, было создание отдельной микросхемы асинхронного последовательного порта, называемого универсальным асинхронным приемопередатчиком (Universal Asynchronous Receiver Transmitter — UART). К тому времени, когда начались разработки процессоров, эта микросхема UART уже производилась вовсю. В большинстве ПК, даже выпущенных в 70-х годах, имелся последовательный порт на базе микросхемы UART, также как и в большинстве современных систем. Помимо узлов, отвечающих за побитовую передачу данных, контроль ошибок и обработку прерываний, большинство микросхем имели встроенный контроллер скорости передачи, который можно было конфигурировать программно для задания требуемой скорости.
Базовая структура микросхемы UART показана на Рис. 12.21. В любой подобной микросхеме можно выделить три основные части. Сдвиговый регистр передатчика преобразует исходные данные из параллельных в последовательные для выдачи через вывод ТХ, обрамляя их старт- и стоп-битами. С этим регистром связан буферный регистр, хранящий данные для последующей передачи. Регистр состояния содержит флаг (TBUF на рисунке), показывающий, что буфер пуст и готов для записи новых данных.
Рис. 12.21. Основные элементы модуля UART
Сдвиговый регистр приемника удаляет из принятой посылки старт- и стоп-биты, перегружая принятые данные в тот или иной буферный регистр. Одновременно с этим устанавливается флаг (RBUF в нашем случае), благодаря чему программа может определить наличие новых данных. Эти данные необходимо прочитать из буферного регистра до сборки следующего пакета, в противном случае возникнет переполнение, и данные будут потеряны.
Прием и передача кадра не взаимосвязаны, т. е. могут перекрываться, однако скорости обмена обычно делаются одинаковыми.
Реальные приемопередатчики UART являются более сложными устройствами, позволяющими, например, передавать данные различной разрядности, а также обеспечивающие обнаружение различных ошибок. При этом, разумеется, усложняется структура соответствующих регистров управления и состояния. Тем не менее в основе модуля последовательного коммуникационного интерфейса (SCI), реализованного в микроконтроллерах PIC и более известного под названием USART (Universal Synchronous-Asynchronous Receiver Transmitter — универсальный синхронно-асинхронный приемопередатчик) (см. Рис. 12.22), лежит все та же архитектура UART. Этот модуль поддерживает два вида последовательного обмена: описанный выше асинхронный, осуществляемый при сброшенном бите SYNC регистра TXSTA[4]Здесь имеется в виду не размер данных, которыми оперирует микроконтроллер, а число битов, использующихся для записи слова команды. — Примеч. пер.
(состояние по умолчанию после сброса), и синхронный (SYNC = 1), при котором старт- и стоп-биты не используются. В последнем случае под линию тактового сигнала задействуется дополнительный вывод RC6/CK — выход при передаче данных и вход при приеме. Вывод RC7/DT используется в качестве линии ввода/вывода данных. При работе в синхронном режиме данные могут посылаться либо побайтно, либо сплошным потоком. Именно из-за возможности работать в синхронном режиме этот модуль и называется USART, а не UART. Сейчас мы все свое внимание уделим асинхронному режиму, поэтому на Рис. 12.22, где показан формат обоих регистров состояния, обозначены только биты, соответствующие этому режиму работы.
Рис. 12.22. Модуль синхронного последовательного интерфейса SCI, сконфигурированный для работы в асинхронном режиме.
Основными элементами модуля USART являются сдвиговые регистры приема и передачи, а также связанные с ними буферные регистры и регистры состояния, Для разрешения работы всего модуля USART необходимо установить бит SPEN регистра состояния приемника RCSTA (RCSTA[7]Разумеется, существует множество других цифровых кодировок, к примеру 6-точечный код Брайля для слепых.
) в 1. Оба вывода RC6 и RC7, использующиеся соответственно для передачи и приема данных, должны быть сконфигурированы как входы.
Передача
Работа передатчика разрешается установкой бита TXEN регистра состояния передатчика TXSTA (TXSTA[5]См., например: Рональд Дж. Точчи, Нил С.Уидмер . Цифровые системы. Теория и практика: 8-е изд.: Пер. с англ. — М.: Издательский дом «Вильямс», 2004.
). Для передачи слова данных необходимо записать его в регистр передатчика TXREG, откуда он будет перегружен в сдвиговый регистр и побитно передан с вывода ТХ. Если требуется работать с 9-битными данными, то битТХ9 (TXSTA[6]В данной книге для отделения целой части числа от дробной используется точка, а не запятая. — Примеч. ред.
) должен быть установлен в 1, а девятый бит данных необходимо записать в 0-й бит того же регистра перед загрузкой младших восьми битов в регистр TXREG. Если сдвиговый регистр передачи не пуст, т. е. передача предыдущего слова еще не закончена, то новое значение останется в буфере TXREG и будет перегружено в сдвиговый регистр только после завершения передачи.
Первый бит регистра состояния TXSTA отображает состояние сдвигового регистра передатчика, тогда как флаг прерывания TXIF, расположенный в регистре PIR1, автоматически устанавливается в 1 при опустошении буфера TXREG (при его готовности к загрузке новых данных). Если это прерывание требуется в программе, необходимо установить соответствующий бит маски TXIE регистра PIE1 (PIE1 [4]Здесь имеется в виду не размер данных, которыми оперирует микроконтроллер, а число битов, использующихся для записи слова команды. — Примеч. пер.
); см Рис. 7.5 на стр. 223. Флаг ТХIF автоматически сбрасывается при записи в регистр TXREG, поэтому нет необходимости вручную обнулять его в процедуре опроса или в обработчике прерывания.
Прием
После обнаружения на выводе RX старт-бита последующие восемь или девять битов задвигаются в сдвиговый регистр приемника, откуда после завершения приема перегружаются в 2-уровневый буферный регистр RCREG, независимо от того, что в этот момент происходит в секции передатчика. Причем принятые данные сохраняются в регистре верхнего уровня, а содержимое последнего автоматически перегружается в регистр нижнего уровня при условии, что в нем отсутствуют данные, ожидающие считывания. При появлении в этом регистре данных устанавливается флаг прерывания от приемника RCIF, который может использоваться для генерации прерывания при установленном бите маски RCIE регистра PIE1 (а также установленных битах GIE и PEIE). При чтении регистра флаг RCIF автоматически сбрасывается. Если при этом в регистре верхнего уровня находились очередные данные, они перегружаются в регистр младшего уровня, и флаг RCIF устанавливается снова.
Если в момент приема очередного слова данных 2-уровневый буфер приемника окажется полон, то устанавливается флаг ошибки переполнения OERR (RCSTA[1]Этот сайт посвящен оригинальному изданию книги на английском языке, и все перечисленные ниже материалы представлены также на английском. — Примеч. ред.
), а принятое значение теряется. При этом оба слова, находящиеся в буфере, по-прежнему доступны для чтения. Однако для сброса флага OERR необходимо сбросить логику приемника, сбросив бит CREN (RCSTA[4]Здесь имеется в виду не размер данных, которыми оперирует микроконтроллер, а число битов, использующихся для записи слова команды. — Примеч. пер.
), а затем установив его снова.
Флаг ошибки кадрирования FERR в RCSTA[2]Имеется в виду оригинальное издание книги на английском языке. — Примеч. ред.
устанавливается в 1, если после приема битов данных не было обнаружено стоп-бита. Флаг FERR (как и девятый бит принятых данных) буферизуется вместе с принимаемыми данными. Поэтому значение указанного флага необходимо проверять перед считыванием содержимого RCREG, поскольку во время этой операции изменяется состояние буфера и, соответственно, значение указанных битов.
Все версии модуля USART позволяют работать с 8- или 9-битными данными независимо при передаче и при приеме. Для последнего необходимо сбросить бит RX9 регистра RCSTA (RCSTA[6]В данной книге для отделения целой части числа от дробной используется точка, а не запятая. — Примеч. ред.
). Обычно этот дополнительный бит используется для контроля четности. Другим применением 9-битных данных является реализация сети из асинхронных устройств. В этом случае девятый бит используется в качестве селектора, показывающего содержимое пакета — данные или адрес устройства. Примитивная сеть, в которой используется этот принцип, изображена на Рис. 12.23. При 8-битном адресе можно адресовать до 255 ведомых устройств (один адрес при этом резервируется для широковещательных вызовов).
Для облегчения работы в подобной сети последние версии модуля US ART можно сконфигурировать таким образом, чтобы при приеме пакета с установленным девятым битом автоматически устанавливался флаг RCIF. Эта функция включается установкой в 1 бита ADDEN (RCSTA[3]New Scientist , vol.59, no. 2141, 4 July 1998, p.139.
). При одновременно установленных битах ADDEN и RX9 любой кадр со сброшенным старшим битом будет игнорироваться, а принимаемые данные не будут загружаться в буфер приемника. Если же старший бит окажется равным 1, то принятый байт будет скопирован из сдвигового регистра приема в буфер приемника с одновременной установкой флага RCIF. Ведомое устройство может прочитать этот адрес из RCREG, при этом флаг RCIF будет сброшен. Если адрес верен, то ведомый может сбросить бит ADDEN и принимать все последующие кадры данных обычным образом. При этом ведомый может продолжать контролировать значение девятого бита в RX9D, прекращая прием при обнаружении кадра с установленным 9-м битом.
Рис. 12.23. Локальная сеть, использующая асинхронный последовательный протокол
Контроллер скорости обмена SPBRG
Этот узел представляет собой программируемый 8-битный счетчик, на выходе которого имеется отключаемый делитель на 4. Конфигурация данного счетчика может задаваться пользователем для получения частот выборки и сдвига, соответствующих желаемой скорости обмена. Отталкиваясь от значения частоты кварцевого генератора микроконтроллера, получаем:
Скорость обмена = (XTAL x 106)/(64 x (X + 1)) — низкоскоростной режим (BRGH = 0),
Скорость обмена = (XTAL x 106)/(16 х (X + 1) — высокоскоростной режим (BRGH = 1),
где X — 8-битное число, находящееся в регистре SPBRG. При использовании низкоскоростного режима значение X определяется из выражения
X = ((XTAL x 106)/64 x (BAUD)) — 1. Так, если при частоте резонатора 20 МГц нам потребуется скорость обмена, равная 9600 бод, то при Х = 31 действительное значение скорости будет равно 9766, т. е. ошибка составит ±1.73 %. При частоте резонатора, равной 20 МГц, максимальная скорость обмена составляет 312 500 бод, а минимальная — 1221 бод. Скорость передачи, равную 1.25 Мбод, можно получить с резонатором частотой 20 МГц, используя высокоскоростной режим модуля при Х = 1.
В действительности контроллер скорости обмена формирует тактовый сигнал с частотой в 16 раз больше заданной скорости, чтобы модуль мог за время передачи бита сделать три выборки в окрестностях середины битового интервала и принять мажоритарное решение о значении этого бита. Такая схема увеличивает надежность передачи данных в системах с высоким уровнем помех.
Чтобы проиллюстрировать использование модуля USART, перепишем наши подпрограммы GETCHAR и PUTCHAR таким образом, чтобы они использовали аппаратные возможности модуля. Первым делом (в основной программе) мы должны сконфигурировать контроллер скорости обмена, а также регистры управления и состояния приемника и передатчика. Предполагая, что константы XTAL и BAUD уже определены программистом, возложим вычисление числа X, которое мы впоследствии запишем в регистр SPBRG, на ассемблер. С учетом всего сказанного инициализационный код будет выглядеть следующим образом:
include "p16f877а. inc"
#define BAUD d’4800’ ; Скорость обмена 4800 бод
#define XTAL d’8’ ; 8-МГц резонатор
#define X ((XTAL*d’1000000’)/(d’64’*BAUD))-1
START bsf STATUS,RP0 ; Переключаемся в 1-й банк
movlw X ; Загружаем X в контроллер скорости обмена
movwf SPBRG
movlw b’00100000’ ; 8 битов данных, передатчик включен,
movwf TXSTA ; низкоскоростной режим
bcf STATUS,RP0 ; Возвращаемся обратно в 0-й банк
movlw b’10010000’ ; USART включен, 8-битные данные
movwf RCSTA ; Приемник включен
Заметьте, что для корректной работы модуля USART в микроконтроллерах линейки PIC16F87X требуется, чтобы оба вывода RX и ТХ были сконфигурированы как входы. Поскольку после сброса микроконтроллера выводы находятся в этом состоянии по умолчанию, в приведенном фрагменте отсутствуют команды для конфигурирования регистра TRISC. Как было отмечено в примечании на стр. 426, другие члены семейства могут потребовать других настроек для выводов RX и ТХ.
Код самих подпрограмм приведен в Программе 12.15. Подпрограмма PUTCHAR просто опрашивает флаг TXIF, ожидая его установки, а затем копирует байт данных в регистр передачи TXREG.
Подпрограмма приема символа GETCHAR будет немного сложнее из-за наличия контроля ошибок. Подпрограмма постоянно опрашивает состояние флага RCIF, который устанавливается при наличии доступных для чтения данных. При отсутствии каких-либо проблем в переменной ERR возвращается число h’00’, при возникновении ошибки кадрирования возвращается -1, при переполнении —2, а при одновременном обнаружении обеих ошибок —3. В последних двух случаях осуществляется сброс бита OERR посредством сброса логики приемника. После проверки на наличие ошибок данные считываются из буфера приемника RCXREG. Контроль ошибок всегда выполняется перед считыванием данных, чтобы избежать непреднамеренного изменения этих флагов регистра состояния приемника.
Программа 12.15 . Подпрограммы ввода/вывода с использованием модуля USART
; *****************
* ФУНКЦИЯ: Передает 8-битное значение по асинхронному каналу *
* РЕСУРСЫ: Модуль USART *
* ВХОД: 8-битное значение в DATA_OUT *
* ВЫХОД: Содержимое DATA_OUT не изменяется, байт передан *
; ******************
PUTCHAR btfss PIR1,TXIF ;Проверим, полон ли буфер передатчика?
goto PUTCHAR ;ЕСЛИ нет, ТО проверим снова
movf DATA_OUT,w ;ИНАЧЕ считываем значение
movwf TXREG ;и копируем его в регистр передатчика
return
; ******************
; * ФУНКЦИЯ: Принимает 8-битное значение по асинхронному каналу *
; * РЕСУРСЫ: Модуль USART *
; * ВХОД: Нет *
; * ВЫХОД: Принятый байт — в DATA_IN. *
; * ВЫХОД: ERR = 00, если не было ошибок. При ошибке *
; * кадрирования ERR = -1, при переполнении ERR = -2, *
; * при наличии обеих ошибок ERR = -3 *
; *******************
GETCHAR clrf ERR ; Обнуляем признак ошибки
btfss PIR1,RCIF ; Проверим, есть ли символ?
goto GETCHAR ; ЕСЛИ нет, ТО проверим снова
; Обработка ошибок
btfss RCSTA,FERR ; Была ошибка кадрирования?
goto CHECK_OERR ; ЕСЛИ нет, ТО проверим на переполнение
movlw -1 ; ИНАЧЕ фиксируем ошибку
CHECK_OERR
btfss RCSTA,OERR ; Было переполнение?
goco GET_EXIT ; ЕСЛИ нет, TO завершаем обработку ошибок
decf ERR,f ; Иначе фиксируем ошибку
decf ERR,f
bcf RCSTA,CREN ; и сбрасываем логику приемника
bcf RCSTA,CREN
GET_EXIT
movf RCREG,w ; Читаем байт данных
movwf DATA_IN ; и помещаем во временный регистр
return
end
В некоторых системах нельзя позволить процессору тратить машинное время на ожидание символа, который придет неизвестно когда. Специально для таких случаев можно было бы написать альтернативную подпрограмму приема, назвав ее, скажем, getch. Эта подпрограмма будет возвращать ERR = +1 при отсутствии данных в буфере. И все же наилучшим выходом из ситуации будет генерация прерывания при обнаружении входящего символа, а не простой опрос флага этого прерывания.
В языке Си каналы асинхронного обмена могут использоваться в качестве стандартных потоков ввода/вывода. Что же касается конкретно компилятора CCS, то в нем имеется директива #use rs232 (), посредством которой можно сообщить компилятору, какие выводы будут использоваться для приема и передачи данных, а также какой должна быть скорость обмена. Стандартные Си-функции ввода/вывода, такие как printf (), используют эти выводы для связи со стандартным каналом. С помощью данного компилятора можно реализовать множество не связанных между собой асинхронных каналов.
В качестве примера, в Программе 12.16 приведена реализация на языке Си асинхронной дуплексной связи с терминалом (см. Рис. 12.25), работающим на скорости 9600 бод. К выводу RB0 подключена кнопка, и, когда оператор посылает микроконтроллеру символ ‘G’, тот начинает опрашивать состояние этой кнопки. При ее замыкании (появлении на выводе сигнала НИЗКОГО уровня) терминал извещает оператора строкой «Кнопка 1 замкнута». Для ввода и вывода данных воспользуемся стандартными функциями printf () >) и getch () >).
Программа 12.16. Использование дуплексного асинхронного канала в языке Си
#include <16f877a.h>
#use delay (clock = 20000000) /* Сообщаем компилятору частоту резонатора (20 МГц) */
/* Сообщаем компилятору о требуемой скорости обмена и используемых выводах */
#use rs232(baud=9600, xmit=PIN_A1, rcv=PIN_A2)
#bit SWITCH1 =6.0 /* Кнопка подключена к RB0 */
void main(void)
{
while(TRUE)
{
if(getch() == ’G’)
{
while (SWITCH1) {;} /* Пока кнопка разомкнута (1), ничего не делаем */
printf("Кнопка 1 замкнута \n");
}
}
}
Поскольку в качестве выводов приемника и передатчика используются выводы RA1 и RA2, компилятор сгенерирует код программно-реализованного UART, подобный использованному нами в Программе 12.14. Именно по этой причине компилятору необходима информация о частоте кварцевого резонатора микроконтроллера — для формирования требуемых задержек. Если же мы укажем выводы RC6 и RC7, то для реализации последовательного интерфейса компилятор автоматически воспользуется встроенным модулем USART. В нашем примере для реализации программного UART потребовалось 146 команд, тогда как при использовании модуля UART размер программы составил всего 74 команды.
Однако для реализации полноценного соединения недостаточно одного только выбора подходящего протокола. При работе микроконтроллеров PIC используются напряжения нормальных логических уровней и токи, которые предназначены для организации соединений на расстояниях не более 30 см (1 фут). Хотя при соблюдении определенных правил это расстояние можно значительно увеличить, при относительно больших скоростях обмена должны использоваться принципиально другие методы формирования сигналов.
В эпоху электромеханических терминалов широко использовался интерфейс «Токовая петля 20 мА», ставший стандартом де-факто. В этом интерфейсе для обозначения состояний лог. 0 и лог. 1 использовались разные значения тока: 0 мА и 20 мА соответственно. Привязка к току, а не напряжению позволяла избежать влияния потерь в линии (поскольку вытекающий ток должен быть равен втекающему), и, кроме того, тока такой величины было достаточно для непосредственного управления электромагнитным реле приемного устройства.
Источники тока реализуются посредством источников высокого напряжения, последовательно с которыми включается большое сопротивление. Именно из-за последнего величины постоянных времени получаются настолько большими, что хотя они и удовлетворяли требованиям эпохи скоростей в 110 бод, но для использования в электронных терминалах, UART и модемах не годятся. В качестве стандартного интерфейса для подключения терминального оборудования (Data Terminal Equipment — DTE) к устройствам передачи данных (Data Circuit Equipment — DCE), как правило к модемам, в 1969 году был предложен интерфейс RS-232. В спецификации этого интерфейса были определены не только различные уровни сигналов, как показано на Рис. 12.24, а, но и различные линии управления и квитирования, некоторые из которых показаны на Рис. 12.24, г и Рис. 12.25. Например, выдачей активного уровня на линию квитирования готовности к передаче (Clear То Send — CTS) модем может сообщить локальному терминалу о том, что удаленный терминал освободил телефонную линию. Для организации дуплексной линии связи необходимо две линии данных плюс общий провод как опция.
Рис. 12.24. Некоторые варианты последовательной передачи данных
Стандарт RS-232 рассчитан на дальность до 15 м (50 футов) при максимальной скорости 20 Кбод, что достигается использованием для передачи лог. 0 (это состояние линии часто называется space) напряжения +12 В, а для передачи лог. 1 (mark) — напряжения —12 В. Минимальное же напряжение, при котором приемник может распознавать состояние линии, составляет ±3 В. Интерфейс стандарта RS-423 (1978 г.), показанный на Рис. 12.24, б, похож на RS-232, но позволяет управлять несколькими (до десяти) приемными устройствами на расстоянии 1.2 км (6000 футов) при скорости до 1 Кбод и на расстоянии до 12 м (40 футов) при скорости 100 Кбод.
Интерфейсы RS-232 и RS-423 являются несимметричными (или небалансными), поскольку приемник контролирует потенциал между сигнальной линией и локальным общим проводом. И хотя «земли» передатчика и приемника, как правило, объединяются между собой, импеданс этой линии при ее значительной протяженности может привести к появлению большой разности потенциалов на ее концах, в результате чего уменьшится помехоустойчивость. Более того, любая наведенная извне помеха вносит в различные сигналы неодинаковые искажения, что вызвано неидентичностью электрических характеристик сигнальных линий. Поэтому такие интерфейсы и называются несимметричными.
Интерфейсы RS-422 (1978 г.) и RS-485 (1983 г.) относятся к классу симметричных. В таких интерфейсах каждая линия связи состоит из двух проводников, обычно свитых между собой, называемых витой парой. Логические уровни в такой линии представляются разностью потенциалов между проводниками, а не относительно общего провода. Обозначим проводники буквами А и В, тогда логическому нулю будет соответствовать соотношение А < В, алогической единице — А > В. На стороне приемника разницы потенциалов, превышающей значение ±200 мВ, будет достаточно для устойчивого распознавания логического уровня, при том, что передатчик обычно формирует сигналы ΔV= ±5 В. Так как проводники А и В имеют одинаковые электрические характеристики и свиты друг с другом, они совершенно идентичны для наводимых помех. Поскольку один и тот же сигнал окажется приложенным к обоим проводникам, а приемник контролирует разность потенциалов, отсекая синфазное напряжение величиной до ±7 В, очевидно, что помехоустойчивость такой симметричной линии связи гораздо выше, чем несимметричной. Имеющиеся в продаже кабели с витыми парами, используемые в локальных сетях (Local Area Network — LAN), обычно содержат три или четыре пары проводников, причем каждая пара имеет свой шаг скрутки. Это сделано для того, чтобы уменьшить уровень перекрестных помех между линиями. В шине USB, применяющейся в ПК, для передачи сигнала тоже используется симметричная линия связи.
Основным отличием между стандартами RS-422 и RS-485 является возможность использования в последнем нескольких передатчиков, так же как и приемников, что позволяет реализовать многоабонентскую сеть. Поскольку в каждый момент времени может быть активен только один передатчик, буфер передатчика должен иметь вход разрешения для выбора ведущего устройства. На линии RS-422 может быть только один передатчик, поэтому нет необходимости его запрещать.
Интерфейс RS-232 изначально был разработан для организации соединения терминал-модем, однако в настоящее время сфера его применения намного шире (см. Рис. 12.25). На Рис. 12.24, г показана простая дуплексная система с частотной манипуляцией (Frequency Shift Keying — FSK), в которой состояния mark/space в одном канале представляются сигналами с частотами 1070/1270 Гц, а в другом — 2025/2225 Гц. Указанные частоты хорошо подходят для передачи по обычной телефонной линии, имеющей полосу пропускания 300 Гц…3.4 кГц. Линии квитирования DCD (обнаружение несущей), CTS (готовность к приему) и RTS (готовность к передаче) используются для аппаратного управления потоком.
В большинстве модемов в настоящее время используется фазовая манипуляция (Phase Shift Keying — PSK). При этом для кодирования 3-битных групп кодов в одном временном интервале обычно используется не менее восьми различных фаз сигнала одной и той же частоты, сдвинутых друг относительно друга на 45°. За счет этого можно увеличить скорость передачи данных при той же скорости передачи сигналов, хотя и ценой снижения помехоустойчивости.
В качестве примера, на Рис. 12.25 показано соединение между микроконтроллером PIC и последовательным портом компьютера (или любым другим устройством, имеющим порт RS-232). Микросхема МАХ233 компании Maxim является сдвоенным приемопередатчиком RS-232, осуществляющим двустороннее преобразование сигналов + 12 В <=> 0 В (лог. 0) и -12 В <=> +5 В (лог. 1). Если линии квитирования не используются, что обычно имеет место при реализации простейших линий связи, ПК можно «обдурить», соединив выводы порта так, как показано на Рис. 12.25 (выход RTS соединен с входом CTS, а выход DTR — с входом DSR). В этом случае ПК будет считать, что последовательный интерфейс постоянно готов к приему данных. Микросхема МАХ232 имеет в общей сложности по два буфера на прием и на передачу, поэтому при необходимости ее можно будет использовать также для буферирования линий квитирования.
На Рис. 12.25 тот же микроконтроллер управляет полудуплексной линией связи стандарта RS-485, используя преобразователь уровней МАХ485 фирмы Maxim. Оба буфера имеют собственные входы разрешения с противоположными активными уровнями (буфер передатчика — ВЫСОКИЙ, а буфер приемника — НИЗКИЙ). Микроконтроллер может включать соответствующий буфер в зависимости от направления обмена. Также с помощью микросхемы МАХ485 можно реализовать дуплексный канал с использованием двух линий связи.
Данные по интерфейсу RS-485 можно передавать и по синхронному протоколу. При этом, разумеется, необходимо будет выделить отдельный буфер для передачи тактового сигнала.
Рис. 12.25. Взаимодействие с ПК по интерфейсу RS-232 и с внешним миром по интерфейсу RS-422/485
Примеры
Пример 12.1
В Примере 11.2 мы написали подпрограмму, сравнивающую фиксированное значение TRIP с байтом, считанным из порта В. В ряде случаев может потребоваться подстройка программы под изменяющиеся условия работы путем модификации порогового значения по командам извне. Вместо того чтобы использовать второй порт ввод/вывода, было предложено передавать новое значение в последовательном виде на вывод RA4, используя вывод RA3 для подключения к линии тактовых сигналов. Предполагая, что состояние линии данных стабильно при ВЫСОКОМ уровне на линии тактового сигнала, напишите подпрограмму, считывающую новое значение и записывающую его в ячейку TRIP.
Решение
Один из возможных вариантов решения этой задачи приведен в Программе 12.17. Эта подпрограмма отслеживает появление ВЫСОКОГО уровня на линии тактового сигнала, при котором, согласно условию задания, сигнал на линии данных стабилен. Изменяя значение бита переноса в соответствии с состоянием линии данных и выполняя операцию сдвига через перенос, осуществляется побитовая загрузка нового значения в память. Причем очередная итерация цикла завершается только после того, как на линии тактового сигнала вновь появляется НИЗКИЙ уровень.
Программа 12.17. Подпрограмма изменения порогового значения из Программы 11.6
; ***************
; * ФУНКЦИЯ: Задвигает значение порога TRIP, которое затем используется в качестве операнда п/п СОМР *
; * ВХОД: Изменение значения битов данных на RA4 происходит при НИЗКОМ уровне на RA3 *
; * ВЫХОД: COUNT = 00, принятое значение — в TRIP *
; ***************
SER_TRIP movlw 8 ; Счетчик битов
movwf COUNT
SER_TRIP_LOOP
btfss PORTA,3 ; Ждем 1 на такт, линии
goto SER_TRIP_LOOP
bcf STATUS,С ; Обнуляем флаг переноса
btfsc PORTA,4 ; На линии данных 1?
bsf STATUS,С ; ЕСЛИ да, ТО устанавливаем флаг переноса
rlf TRIP,f ; Вдвигаем бит
SER_TRIP_LOOP2
btfsc PORTA,3 ; Дожидаемся появления 0 на такт, линии
goto SER_TRIP_LOOP2
decfsz COUNT,f
goto SER_TRIP_LOOP
return
Эта подпрограмма похожа на подпрограмму SPI_READ (см. Программу 12.3), за исключением того, что тактовый сигнал формируется внешним устройством, т. е. микроконтроллер PIC в данном случае выступает в роли ведомого. В реальных системах, где ведомый микроконтроллер должен обладать возможностью сообщать ведущему о необходимости передачи нового байта, такая схема может вызвать определенные проблемы. Указанную возможность можно реализовать, используя дополнительную линию порта ввода/вывода для передачи квитирующего сигнала CTS. Этот сигнал будет генерировать прерывание на стороне ведущего и инициировать обмен. Конечно же, этим ведущим может быть другой микроконтроллер PIC, и в этом случае мы получим очень простой вариант объединения двух микроконтроллеров. При использовании микроконтроллера с встроенным последовательным портом прерывания могут генерироваться автоматически — такой подход наиболее часто используется для реализации многопроцессорных сетей.
Пример 12.2
Напишите подпрограмму I2C_IN, обратную по своему действию подпрограмме I2C_OUT из Программы 12.9. Предполагается, что в вашем распоряжении имеются те же переменные, а принятое значение должно сохраняться в регистре DATA IN.
Решение
Подпрограмма I2C_IN, код которой приведен в Программе 12.18, загружает принимаемое значение в регистр DATA_IN посредством восьми операций сдвига через флаг переноса; значение флага соответствует состоянию вывода SDA. Одновременно на линии тактового сигнала SCL формируются импульсы в соответствии со спецификацией шины I2С, как и в подпрограмме I2C_OUT из Программы 12.9. В соответствии с этим протоколом ведущий приказывает ведомому остановить посылку данных путем выдачи на линию SDA ВЫСОКОГО уровня во время 9-го тактового импульса (см. Рис. 12.13). Наличие во время этого временного интервала НИЗКОГО уровня на линии данных называется АСК (подтверждение), а наличие ВЫСОКОГО уровня — NACK (нет подтверждения). Наша подпрограмма может генерировать оба сигнала, в зависимости от значения переменной ACKNO, которое задается вызывающей подпрограммой. Если при вызове подпрограммы содержимое регистра ACKNO равно нулю, то после приема 8-го бита данных отсылается АСК. Соответственно, любое ненулевое значение регистра ACKNO приведет к отсылке ведомому сигнала NACK. После получения этого сигнала ведомый прекратит передачу и начнет отслеживать появление на шине состояний СТАРТ/СТОП.
Программа 12.18. Подпрограмма получения байта по шине I 2 С
; **************
; * ФУНКЦИЯ: Принимает байт от ведомого, отсылая в ответ АСК или NACK *
; * ВХОД: ACKNO = 00 для отсылки АСК, ИНАЧЕ NACK *
; * РЕСУРСЫ: п/п START и STOP, макрокоманда Delay_600 *
; * ВЫХОД: Байт данных, посланный ведомым — в DATA_IN, ведомому отослан АСК или NACK, на SCL — НИЗКИЙ уровень *
; **************
I2C_IN bcf INDF,SCL ; Гарантируем наличие НИЗКОГО уровня на SCL
movlw 8 ; Счетчик цикла = 8
movwf COUNT
I2C_IN_LOOP
bcf INDF,SCL ; Формируем на тактовой линии
Delay_600 ; отрицательный импульс
Delay_600
bsf INDF,SCL ; минимальной длительности
bcf STATUS,С ; Сбросим флаг переноса
btfsc INDF,SDA ; Проверяем значение принятого бита
bsf STATUS,С ; ЕСЛИ 1, ТО устанавливаем флаг С
rlf DATA_IN,f ; и вдвигаем его в регистр
decfsz COUNT,f ; Декрементируем счетчик цикла
goto I2C_IN_LOOP ; и повторяем восемь раз
; Теперь посмотрим, что надо отослать (АСК или NACK)
bcf INDF,SCL ; Выставим на SCL НИЗКИЙ уровень
bsf INDF,SDA ; Высвободим линию данных (NACK)
movf ACKNO,f ; Проверим регистр
btfsc STATUS,Z ; ЕСЛИ не равно 0, ТО ничего не делаем
bcf INDF,SDA ; ИНАЧЕ выставляем на линию данных НИЗКИЙ уровень (АСК)
Delay_600 ; Удерживает на тактовой линии
Delay_600 ; НИЗКИЙ уровень
bsf INDF,SCL ; Теперь выставляем ВЫСОКИЙ уровень
Delay_600
bcf INDF,SCL ; На линии SCL оставляем НИЗКИЙ уровень
return
Пример 12.3
Во многих микроконтроллерных устройствах требуется сохранять данные в энергонезависимой памяти для того, чтобы считывать их после повторного включения. В качестве примера можно указать счетчик суммарного пробега, пройденного автомобилем, значение которого должно сохраняться независимо от состояния аккумулятора. Обычно такого рода данные хранятся в EEPROM-памяти, которая была подробно описана на стр. 43. Хотя во многих микроконтроллерах PIC имеется встроенный модуль EEPROM, о котором мы поговорим в главе 15, его емкость ограничена в лучшем случае 256 байтами. При больших объемах необходимо задействовать внешние микросхемы EEPROM. Большинство таких микросхем используют интерфейс SPI или I2С, как, например, микросхема 24LCXXX, применяющаяся в схеме на Рис. 12.26. Микросхемы EEPROM с последовательным интерфейсом серии 24LCXXX, выпускающиеся в 8-выводных корпусах, имеют емкость от 1 Кбит (24LC01B) до 512 Кбит (24LC512), организованных побайтно; т. е. от 128 байт до 64 Кбайт.
Рис. 12.26. Применение I 2 С-совместимых микросхем EEPROM серии 24ХХХ
Микросхемы EEPROM серии 24ХХХ имеют следующие характеристики:
• I2С-совместимый интерфейс с максимальной частотой 400 кГц (VDD = 5 В) и 100 кГц при(VDD = 2.5 В.
• Возможность защиты содержимого микросхемы от записи (режим ПЗУ), используя вывод WP.
• Типичная длительность цикла записи — 2 мс.
• Долговечность — не менее 1 000 000 циклов записи на ячейку.
• Ток потребления — 3 мА в режиме записи, 1 мА в режиме чтения и 100 мкА в режиме ожидания.
• Встроенный генератор высокого напряжения для программирования.
На примере микросхемы 24LC01B покажем, какие операции необходимо выполнить для инкрементирования содержимого трех ячеек, расположенных в младших адресах, где хранится суммарный путь в милях или километрах (единица измерения меняется в зависимости от рынка, для которого предназначен автомобиль). Предположим, что каждый километр/милю генерируется прерывание для микроконтроллера и что наш код является частью процедуры обработки прерывания. Кроме того, в нашем распоряжении имеются ресурсы, используемые подпрограммами, коды которых приведены в Программах 12.9 и 12.18.
Решение
Прежде чем приступить к написанию собственно кода, необходимо познакомиться с протоколом обмена, поддерживаемым микросхемами серии 24ХХХ. Этот протокол в виде временных диаграмм сигналов на линии данных приведен на Рис. 12.27.
Рис. 12.27. Временные диаграммы операций чтения и записи микросхем EEPROM
Инициирование обмена всегда осуществляется ведущим (микроконтроллером), который формирует на шине состояние СТАРТ, после чего передает управляющий байт. В этом байте содержится адрес ведомого 1010, адрес конкретной микросхемы А2А1А0, а также бит R/W¯: . Однако, хотя в управляющий байт и включены биты адреса (а соответствующие им выводы микросхемы показаны на Рис. 12.26), в последних версиях микросхем EEPROM малого объема возможность изменения адреса микросхемы не реализована. Так сделано потому, что в случае необходимости увеличения объема памяти гораздо проще и эффективнее заменить микросхему на другую, большей емкости, поскольку цоколевка у всех микросхем серии одинакова. Так, заменив микросхему 24LC01B на 24LC08B, мы получим восьмикратное увеличение объема без вмешательства в схему самого устройства. Микросхемы же большей емкости, такие как 24LC256, используют выводы адреса для расширения системы, так как в этом случае придется подключать к шине дополнительные микросхемы. Так, при использовании восьми микросхем 24LC512 мы получим энергонезависимую память объемом 512 Кбайт.
Как правило, после управляющего байта передается значение адреса в памяти, куда будут записываться или откуда будут считываться данные. Если говорить конкретно о микросхеме 24LC01B, то в ней данные организованы в виде 128 однобайтных ячеек, каждая из которых может быть записана или считана независимо от других. То есть в ней используется 7-битный адрес, для передачи которого вполне достаточно одного байта. Эта схема годится также для микросхемы 24LC02B, однако для всех остальных микросхем требуется адрес, разрядность которого больше 8 бит. В микросхемах от 24LC04 до 24LC16 для передачи трех старших битов адреса используются биты А[2:0] управляющего байта, в результате чего разрядность адреса увеличивается до 11 бит. Микросхемы EEPROM, имеющие объем более 16 Кбит (24LC32 и далее), требуют уже двух байтов адреса, которые передаются вслед за управляющим байтом.
Байты адреса посылаются в EEPROM в пакетах записи, как показано на Рис. 12.27, а, т е. со сброшенным битом R/W¯ управляющего байта. Если в данную ячейку необходимо записать байт данных, то он будет передан сразу же после байта адреса, а затем на шине будет сформировано состояние СТОП. Если же до формирования состояния СТОП будет передано более одного байта, то они будут сохранены во внутреннем буфере небольшого объема, а реальное программирование начнется только после появления состояния СТОП. Микросхема 24LC02B может запомнить до восьми байтов (одну страницу) данных, инкрементируя при получении очередного байта три младших бита адреса. При переходе через границу страницы ранее загруженные значения перезаписываются. Размер страницы зависит от модели устройства. Например, в 24LC256 используются 64-байтные страницы. На Рис. 12.27, а показан процесс записи трех байтов в микросхему 24LC01B. Поскольку используемые нами ячейки расположены в младших адресах (h’00’, h’01’ и h’02’), то переполнения не произойдет.
После обнаружения микросхемой состояния СТОП запускается процесс записи буферированных данных в заданные ячейки. Длительность процесса программирования в микросхемах семейства 24LCXXX составляет от 2 до 5 мс. Если в течение этого времени ведущий попытается обратиться к микросхеме, то она при получении первого (управляющего) байта отошлет NACK, что можно использовать в качестве индикатора занятости. В Программе 12.19 этот бит проверяется при посылке управляющего байта.
Процесс чтения содержимого EEPROM, показанный на Рис. 12.27, б, немного сложнее. Как и в предыдущем случае, транзакция начинается посылкой адреса устройству. Затем ведущий формирует состояние ПОВСТАРТ, после чего повторно передает управляющий байт с установленным битом R/W¯, свидетельствующий о том, что ведущий будет работать в качестве приемника данных. После этого микросхема EEPROM отсылает байт, расположенный по адресу, указанному ведущим, который при получении байта выставляет подтверждение. Процесс передачи и приема байтов (с постоянным инкрементированием адреса) будет продолжаться до тех пор, пока ведущий в ответ на очередной байт не выставит NACK. После этого ведомый освободит шину и ведущий сможет сформировать состояние СТОП. Если первый пакет, содержащий адрес ячейки памяти, был опущен, то чтение начнется с адреса, на единицу большего того, к которому производилось последнее обращение.
Программа, код которой приведен в Программе 12.19, в точности выполняет все операции, показанные на Рис. 12.27. После посылки начального адреса h’00’ микроконтроллер переходит в режим чтения и считывает три последовательно расположенных байта из EEPROM-памяти. Чтение завершается возвратом NACK с последующим формированием состояния СТОП. Поскольку все три байта расположены друг за другом, используется автоматическое инкрементирование адреса. После инкрементирования 3-байтного значения оно передается обратно в EEPROM после повторной передачи адреса 1-й ячейки (h’00’). Процесс завершается формированием состояния СТОП.
Программа 12.19. Инкрементирование значения одометра, хранящегося в энергонезависимой памяти
EXTRA_MILE ; Считываем три байта, хранящиеся по адресам 00:01:02h
call START ; Начинаем с формирования состояния СТАРТ
;1-й управляющий байт — адрес микросхемы ------------
movlw b’10100000’ ; Адрес ведомого, ведущий-передатчик
movwf DATA_OUT ; Копируем в буферный регистр
call I2C_OUT ; Передаем
movf ERR, f ; Проверяем наличие подтверждения
btfsc STATUS,Z ; ЕСЛИ ноль, ТО продолжаем
goto EXTRA_MILE ; ИНАЧЕ пробуем снова
; Адрес 00 --------------
clrf DATA_OUT ; Формируем адрес
call I2C_OUT ; Передаем
; 2-й управляющий байт для инициирования операции чтения --------
call START
movlw b’10100001’ ; Адрес ведомого, ведущий-приемник
movwf DATA_OUT ; Копируем в буферный регистр
call 2C_OUT ; Передаем
; Теперь считываем три байта данных
clrf ACKNO ; Разрешаем формирование подтверждения
call I2C_IN ; Считываем старший байт из ячейки с адресом 00h
movf DATA_IN,w ; Берем байт
movwf MSB ; и сохраняем его в памяти
call I2C_IN ; Считываем средний байт из ячейки с адресом 01h
movf DATA_IN,w ; Берем байт
movwf NSB ; и сохраняем его в памяти
incf ACKNO,f ; Выставить NACK
call I2C_IN ; Считываем старший байт из ячейки с адресом 02h
movf DATA_IN,w ; Берем байт
movwf LSB ; и сохраняем его в памяти
call STOP ; Завершаем операцию чтения
; Теперь инкрементируем 3-байтное число
incf LSB,f ; Прибавляем единицу
btfss STATUS,Z ; Проверяем на ноль
goto PUT_BACK ; ЕСЛИ не 0, ТО продолжаем
incfsz NSB,f ; Инкрементируем средний байт
goto PUT_BACK ; ЕСЛИ не 0, ТО продолжаем
incf MSB,f
PUT_BACK call START ; Начинаем операцию записи
movlw b’10100000’ ; Пакет записи
movwf DATA_OUT
call I2C_OUT
clrf DATA_OUT ; Адрес 00h
call I2C_OUT
movf MSB,w ; Берем новое значение старшего байта
movwf DATA_OUT
call I2C_DUT
movf NSB,w ; Берем новое значение среднего байта
movwf DATA_OUT
call I2C_OUT
movf LSB,w ; Берем новое значение младшего байта
movwf DATA_OUT
call I2C_OUT
call STOP
Пример 12.4
Взяв за основу базовый принцип асинхронной передачи данных и дополнив его некоторыми принципами, лежащими в основе синхронного протокола I2С, мы сможем организовать асинхронную передачу данных в обоих направлениях по одной-единственной линии связи (в полудуплексном режиме). Одним из примеров такого скрещивания является интерфейс 1-WireTM, характеристики которого показаны на Рис. 12.28.
В схеме, приведенной на Рис. 12.28, а, используется микросхема цифрового термометра DS18S20 (Maxim/Dallas), управление которой осуществляется посредством одной линии порта микроконтроллера, выступающего в качестве ведущего шины 1-Wire.
Рис. 12.28. Взаимодействие с микросхемой цифрового термометра DS18S20 (1-Wire)
Микросхема DS18S20 имеет следующие характеристики:
• Диапазон измеряемой температуры от —55 до +85 °C с шагом 0.5 °C; результат представляется в виде 16-битного числа со знаком.
• Точность измерения ±0.5 % в диапазоне -10…+85 °C.
• Время преобразования — не более 750 мс.
• Нулевой ток потребления в режиме ожидания.
• Может питаться от линии данных, диапазон напряжения питания от +3 до +5.5 В.
• Возможность работы в многоточечной сети.,
Выполнение различных операций, поддерживаемых термометром, таких как «Преобразование» (h’44’) и «Чтение температуры» (h’BE’), инициируется ведущим посылкой соответствующих 8-битных значений. Процесс передачи каждого из этих значений начинается с посылки старт-бита (), за которым следуют восемь слотов записи, как показано на Рис. 12.28, б. Как и в случае шины I2С, состояние ВЫСОКОГО уровня на линии данных DQ формируется за счет ее подтяжки к шине питания, соответственно ведущий имитирует посылку лог. 1 переключением линии порта на вход (см. Рис. 12.14,б). В этом состоянии ведущий может контролировать линию на предмет данных, посылаемых ведомым, как показано на Рис. 12.28, в.
Для нашего примера потребуется написать две подпрограммы, одна из которых будет передавать байт в ведомое устройство, а другая — считывать один байт из него.
Решение
Из Рис. 12.28, б видно, что процесс записи одного бита в ведомое устройство состоит из следующих этапов:
1. Ведущий инициирует процесс обмена, выставляя на линию данных НИЗКИЙ уровень на время не менее 1 мкс.
2. Ведущий либо оставляет на линии НИЗКИЙ уровень (запись лог. 0), либо высвобождает линию (запись лог. 1) на время 60…120 мкс.
3. Ведомый считывает состояние линии через 15…45 мкс после начала слота.
4. Ведущий освобождает линию (если он записывал лог. 0) на время не менее 1 мкс для приведения системы в исходное состояние.
В подпрограммах, код которых приведен в Программе 12.20, предполагается, что вывод порта, управляющий линией данных DQ, уже сконфигурирован так же, как и выводы, управляющие линиями шины I2С (см. стр. 396), — жесткий НИЗКИЙ уровень и открытый выход, подтянутый к линии питания для формирования ВЫСОКОГО уровня. Также мы предполагаем, что в нашем распоряжении имеется макрокоманда Delay_us, которая формирует задержку длительностью K мкс, где К — параметр, передаваемый в макрокоманду.
Delay_us macro К; К — длительность задержки в мкс
local DELAY_US_LOOP
movlw (K*XTAL)/(4*3)+1 ; 1~
DELAY_US_LOOP
addlw -1 ; Декрементируем счетчик: N~
btfss STATUS,Z ; до нуля: N + 1~
goto DELAY_US_LOOP ;:2(N — 1)~
endm
Дополнительная операция прибавления единицы в выражении для вычисления количества итераций цикла предназначена для округления К в бóльшую сторону
Выполнение обеих подпрограмм начинается с выставления на линию DQ НИЗКОГО уровня на время не менее 1 мкс, в результате чего на линии формируется состояние СТАРТ Запись каждого бита на шину происходит в слоте длительностью 60…120 мкс и инициируется либо выдачей на линию НИЗКОГО уровня (0), либо ее высвобождением (1). Ведомый считывает состояние линии данных через 15 мкс после начала временного слота. Хотя длительность временного слота и не критична, необходимо быть внимательным, поскольку состояние НИЗКОГО уровня длительностью 480…960 мкс интерпретируется ведомым как команда сброса (см. Вопрос для самопроверки 12.3).
Для передачи одного байта используются восемь временных слотов, разделенных паузами длительностью не менее 1 мкс. Значение, передаваемое в каждом слоте, соответствует значению флага переноса, которое формируется в результате циклического сдвига байта данных DATA_OUT. После восьми циклов сдвига/выдачи процесс завершается.
Чтение байта из ведомого устройства происходит следующим образом:
1. Ведущий инициирует процесс чтения, выставляя на линию НИЗКИЙ уровень на время не менее 1 мкс.
2. Ведущий считывает значение бита, выдаваемое на линию ведомым, которое должно быть корректным в течение 15 мкс после формирования фронта состояния СТАРТ.
3. Через 15 мкс ведомый высвобождает линию, на которой к моменту завершения 60-мкс слота в результате подтяжки должен появиться ВЫСОКИЙ
4. Ведущий ждет не менее 1 мкс перед формированием следующего слота.
Указанный алгоритм реализуется подпрограммой чтения READ_1W, которая считывает состояние линии данных до наступления 15 мкс с момента начала временного слота, т. е. в тот момент, когда напряжение на линии, определяемое ведомым, уже установилось. Значение считанного бита используется для установки/сброса флага переноса, который затем вдвигается в переменную DATA_IN. После восьми операций чтения/сдвига в этой переменной окажется принятый байт данных.
В отличие от протокола I2С, протокол 1-Wire предполагает наличие только одного ведущего устройства. Однако все ведомые устройства имеют уникальный 64-битный адрес, хранящийся во внутреннем ПЗУ. Первые восемь битов обозначают код семейства 1-Wire, к примеру, микросхема DS18S20 имеет код h’10’. Следующие 48 бит являются уникальным серийным номером, а последние восемь битов представляют собой контрольную сумму всех предыдущих байтов.
Программа 12.20. Подпрограммы чтения и записи по шине 1-Wire
; *************
; * ФУНКЦИЯ: Передает 1-байтное значение ведомому 1-Wire *
; * РЕСУРСЫ: Макрокоманда Delay_us, формирующая задержку N мкс *
; * ВХОД: Передаваемй байт в DATA_OUT *
; * ВЫХОД: DATA_OUT обнуляется; W, STATUS изменяются *
; *************
WRITE_1W movlw 8 ; Количество проходов цикла
movwf COUNT
W_LOOP bcf INDF,DAT ; Спадающий фронт — СТАРТ
Delay_us 1 ; Ждем 1 мкс
rrf DATA_OUT,f ; Выдвигаем байт через перенос
btfsc STATUS,С ; Бит равен 1?
bsf INDF,DAT ; ЕСЛИ да, ТО выставляем ВЫСОКИЙ уровень
Delay_us d’60’ ; Удерживаем в течение 60 мкс
bsf INDF,DAT ; Высвобождаем линию
Delay_us 1 ; Ждем 1 мкс
decfsz COUNT,f ; Повторяем восемь раз
goto W_LOOP
return
; ******************
; * ФУНКЦИЯ: Принимает 1-байтное значение от ведомого 1-Wire *
; * РЕСУРСЫ: Макрокоманда Delay_us, формирующая задержку N мкс *
; * ВХОД: Нет *
; * ВЫХОД: Принятый байт в DATA_IN; W, STATUS изменяются *
; ******************
READ_1W movlw 8 ; Количество проходов цикла
movwf COUNT
R_LOOP bcf INDF,DAT ; Спадающий фронт — СТАРТ
Delay_us 1 ; Ждем 1 мкс
bsf INDF,DAT ; Высвобождаем линию
Delay_us 8 ; Ждем 8 мкс, чтобы дать возможность ведомому выставить данные
bcf STATUS,С ; Сбрасываем флаг переноса
bcfsc INDF,DAT ; Проверяем состояние входа
bsf STATUS,С ; ЕСЛИ 1, TO устанавливаем флаг переноса
rrf DATA_IN,f ; Задвигаем бит в регистр
Delay_us d’48’ ; Ждем до конца слота
goto R_LOOP
decfsz COUNT,f ; Повторяем восемь раз
return
; ********************
; * ФУНКЦИЯ: Сбрасывает ведомого 1-Wire *
; *********************
RESET_1W bcf INDF,DAT ; Выставляем НИЗКИЙ уровень
Delay_us d’140’ ; Ждем 480…960 мкс
Delay_us d’140’ ; С помощью макрокоманды можно получить
Delay_us d’140’ ; величину задержки (3*0.2)*255, только
Delay_us d’80’ ; если процессор работает на 20 МГц
bsf INDF,DAT ; Высвобождаем линию
Delay_us d’60’ ; Ведомый выставляет НИЗКИЙ уровень через 15…60 мкс
RESET_LOOP
btfss INDF,DAT ; А затем высвобождает линию
goto R_LOOP ; Ждем, пока на линии не появится ВЫСОКИЙ уровень
return ;
Вопросы для самопроверки
12.1. Перепишите Программу 11.5 со стр. 351, но с использованием модуля SPI, показанного на Рис. 12.4. Подсказка: вместо проверки итоговых 1-байтных значений более эффективным решением может стать побитовая проверка вдвигаемого значения.
12.2. Покажите, как можно подключить четыре АЦП МАХ518 (см. Рис. 12.16) к одной шине I2С и как можно загрузить значение 1-го канала третьего АЦП.
12.3. Обмен по шине 1-Wire начинается с формирования ведущим импульса сброса, при котором ведущий выставляет на линию НИЗКИЙ уровень на время 480…960 мкс, после чего линия высвобождается. В ответ на это ведомый выставляет на линию НИЗКИЙ уровень с задержкой не более 60 мкс. Это состояние удерживается на линии в течение 60…240 мкс, после чего ведомый высвобождает линию. Напишите подпрограмму, выполняющую описанные действия. Предполагается, что в вашем распоряжении имеются ресурсы Программы 12.20.
12.4. Контроль по четности представляет собой метод, при котором значение числа всегда является четным или же нечетным. Для этого на стороне передатчика к слову данных добавляется дополнительный бит, значение которого подобрано таким образом, чтобы удовлетворять указанному критерию. Например, при контроле по нечетности (odd) 8-битного числа Ь’01101111’ оно преобразуется в число Ь’101101111’. Приемник же проверяет полученное значение на предмет его нечетности. Если один (или любое нечетное количество) бит был поврежден из-за помех, возникнет ошибка четности (parity error).
Используя модуль USART микроконтроллера PIC, напишите программу, переключающую модуль в режим передачи 9-битных слов и вычисляющую значение бита для контроля по нечетности содержимого DATA_OUT. Этот бит затем должен заноситься в бит TX9D регистра TXSTA перед загрузкой байта данных в регистр TXREG и его передачей.
12.5. Перепишите подпрограмму GETCHAR из Программы 12.14 в виде процедуры обработки прерывания (назовем ее GETCH). Сравните эти два подхода к решению задачи приема символа.
12.6. Система сбора данных считывает значение температуры каждые 15 мин. Потребляемый ток сведен к минимуму за счет использования низковольтной версии микроконтроллера, работающей при напряжении 3 В, и кварцевого резонатора с частотой 32.768 кГц. В этих условиях ток потребления микросхемы с работающим Таймером 1 составляет не более 70 мкА (типовое значение — 45 мкА). Отсчеты запоминаются во внешней микросхеме EEPROM с интерфейсом 12С, однако питание на нее подается только на время записи — в качестве источника питания EEPROM используется отдельный вывод порта. Устройство должно работать от одного комплекта батарей в течение 6 месяцев, находясь при этом на дне озера. Можете ли вы подобрать подходящую микросхему EEPROM из семейства 24LCXXX и оценить требуемую емкость батареи в мА∙ч?