Автор: WildCat
IIC (I2C) - это сокращение от Inter-IC. I2C - разработка компании Philips, начатая в 80-х годах. В наши дни бытовая техника сделала резкий скачок в сторону усложнения схемотехники. В одном устройстве встречается множество микросхем. Как следствие - запутанная разводка и плотный монтаж печатных плат. Philips решила, что неплохо было бы использовать простую последовательную шину. Вместо широких параллельных шин можно было бы использовать всего два проводника. В такой шине один провод отвечает за данные, а второй - за тактовые импульсы, SDA и SCL соответственно.
I2C стандарт 1992 года использует семибитные адреса, что позволяет одновременное подключение до 128 ИС к одной шине.
Более подробно про этот протокол можно почитать в спецификации I2C.
Адресация в I2C
Число микросхем на одной шине ограничено 128, потому что каждому чипу соответствует 7-битный адрес. Сравните это, например, с домами на одной улице. Если у нескольких домов будет один и тот же номер, то почтальон придет в замешательство, не зная, куда бросить извещение о посылке с деталями.
Адреса задаются частично аппаратно в микросхеме, частично их можно задавать вручную путем подключения соответствующих выводов к питанию/земле. Это позволяет независимо подключать несколько одинаковых микросхем к одной шине.
Для примера рассмотрим известную PCF8574P. Это восьмибитный расширитель портов общего назначения. В нем аппаратно установлен адрес 0100 A2 A1 A0. Часть 0100 уже прошита в чипе, а часть A2 A1 A0 можно задать вручную. Мы можем сделать это, подключая соответствующие выводы к + питания или земле:
Вот тут микросхема слева имеет адрес 0100100, а справа - 0100000.
Стандарты I2C
Изначально стандарт I2C работал с тактовой частотой до 100кГц. В 1992 году был добавлен Быстрый режим с максимальной частотой 400кГц. Также в этом варианте была реализована 10-битная адресация. В 1998 году появился Высокоскоростной режим с максимальной скоростью 3,4 Мбит/с. (вероятно, Philips сочла Мбит/с более удобной величиной, чем килогерцы) Быстрый и высокоскоростной режимы одинаково хорошо работают с частотой и 100кГц.
Подробнее про стандарт
Philips обладает патентом на I2C. Это значит, что любая компания, желающая выпускать микросхемы с поддержкой I2C должна заплатить за это и подписать соглашение.
Большинство микроконтроллеров AVR обладают двухпроводным интерфейсом (угадайте, что это), по крайней мере, так сказано в документации. Это, конечно же, пресловутый I2C. Видимо, эта терминология используется из жадности и нежелания платить деньги Philips за то, чем все и так прекрасно пользуются.
Стандарт I2C предусматривает подтягивающие резисторы на SDA и SCL. Это одна из наиболее распространенных ошибок. Забудьте про подтягивание, и ваша схема будет безупречно не работать. Как правило, хватает резистора в 10кОм. Иногда, при достижении максимальной скорости обмена, можно импользовать более низкие значения. Проверьте даташиты микроконтроллера и вашей микросхемы.
Иногда можно встретить резисторы 100-330 Ом в линиях. Это делается для уменьшения взаимного влияния.
Длина шины I2C не ограничена. Единственное, что общая емкость линии с подключенными устройствами не должны превышать 400 пФ.
Устройство, начинающее передачу, называется Ведущим (Master, мастер), а устройство, к которому оно обращается, - Ведомым (соответственно, Slave, мазохист раб). Во всех наших примерах AT90S2313 выступает в роли ведущего устройства. Ведомое устройство молчит всегда, пока его не спросит ведущее. Тактовые импульсы на SCL тоже обеспечивает ведущее устройство. Мастер отвечает за временные интервалы в передаче. Если ведомое устройство должно вернуть какие-то значения мастеру, он также должен отвечать за все временные интервалы. I2C позволяет работу нескольких ведущих устройств на одной шине, однако во всех наших примерах только один "мастер" и один или несколько "рабов".
Что можно учинить с I2C?
Главное преимущество стандарта - это использование всего двух ног контроллера, что актуально для небольших МК. Конечно, за такое удобство придется платить - передача одного байта займет время передачи 8 бит + время начала/окончания передачи. Поэтому I2C безусловно медленнее параллельной шины. Однако, если вы не гонитесь за мегибитами, I2C - идеальный вариант.
Многие изготовители выпускают занятные микросхемы с I2C. Philips, как изобретатель, лидирует в этом деле. Иногда можно встретить целые модули с I2C интерфейсом (ТВ-тюнеры, дисплеи...).
Что же делать?
Вот что мы решили препарировать:
- Philips PCF8574
- TI PCF8574
- On-Semi JLC1562B
- Philips PCF8591
- Natsem LM76
- Philips graphics display LPH7653
- Linear Technology LT6904
PCF8574 - это восьмибитный расширитель портов общего назначения. Он добавляет 8 дополнительных пинов ввода/вывода. Используйте его для подключения светодиодов, реле или клавиш/клавиатуры. У него есть выход прерывания, на котором появляется низкий уровень после любого изменения состояния входов. После чтения состояния портов его уровень автоматически возвращается в высокий. Это очень удобный способ вызова прерывания при вводе. Контроллер тут же читает что изменилось и принимает решение.
Заметка: Philips выпускает две модификации чипа: PCF8574 и PCF8574A. Они отличаются только адресом!
PCF8574: 0100. A2 A1 A0
PCF8574A: 0111. A2 A1 A0
Изучите эти мелочи подробнее в даташите.
Если вам нужно больше портов ввода/вывода, используйте PCF8575 той же Philips. Это уже 16-битный расширитель портов.
Texas Instruments выпускает одноименные чипы, одинаковые по характеристикам (и корпусам).
On-Semi JLC1562B так же восьмибитный расширитель общего назначения, но у него есть кое-что еще: 6-битный ЦАП и компаратор на младших пяти разрядах. Однако у него нет выхода прерывания. Компаратор совместно с ЦАП и некоторым программным кодом может таинственно превращаться в 6-битный АЦП.
PCF8591 от Philips имеет один 8-битный ЦАП и четыре 8-битных АЦП. Аналоговые входы можно настроить как одиночные, так и на сложение и вычитание сигналов. Вот вам датаshit для ковыряния.
Компания National Semiconductor выпускает микросхему термометра LM76. Сами они заявляют, что назначение этих чипов - 'оценка' температуры, так что не надейтесь на космическую точность. В общем, подробнее в очередном связанном документе. В чипе находится температурный датчик и 12-битный АЦП (+ знаковый разряд).
LPH7653 от Philips - это маленький графический дисплей, вероятно, выдранный из старого телефона. Даташита на него никто никогда не видел, но говорят, что кто-то знает, как управлять им.
LT6904 от Linear Technology - волшебный чип-синтезатор частоты от 1 кГц до 64 МГц.
Конечно, этот список - всего лишь маленький пример того, что нам доступно для экспериментирования. На самом деле в природе существует гораздо больше хороших микросхем, ищите их на сайтах ведущих производителей по запросу I2C.
Стандартные команды чтения/записи
Команды чтения и записи I2C работают с одним байтом за раз. Стандартная процедура записи такая:
I2cstart I2cwbyte ICaddress I2cwbyte Bytetosend I2cwbyte ... I2cstopI2cstart производит сброс линии и заставляет все устроства внимательно слушать. Затем отправляется адрес интересующей нас микросхемы. Она начинает обмен, в это время остальные молчат. Теперь по I2cwbyte отправляются один или несколько байтов. Прием каждого байта подтверждается микросхемой. Наконец, I2cstop освобождает линию.
Стандартная процедура чтения:
I2cstart I2cwbyte ICaddress I2crbyte Bytetoread, Ack I2crbyte Bytetoread, Nack I2cstopТак же, как и в первом случае, I2cstart сбрасывает линию. Все устройства начинают слушать. Затем опять передается адрес интересующей микросхемы. Теперь по I2crbyte принимаются один или несколько байтов. После переменной, в которую происходит считываение, стоит ключевое слово Ack или Nack. Ack сообщает, что нужно считать еще байт, Nack - что этот принятый байт был последним. Об этом подробнее рассказано в документации Philips по I2C, о которой говорилось выше.
Ошибки I2C
В BASCOM зарезервирована переменная Err типа Bit. Если происходит ошибка I2C, в нее помещается 1. Правда никому не известно, что с ней делать. На самом деле, что? Обычно ошибки возникают из-за аппаратных проблем, поэтому вряд ли пользователь сможет что-то сделать.
Простая светодиодная мигалка
Давайте забацаем простую мигалку, используя PCF8574. Соберите на макетной плате схедующую несложную схему:

Затем наберите такую программу:
$regfile = "2313def.dat" $crystal = 4000000 Config Sda = Portd.5 Config Scl = Portd.4 Config I2cdelay = 10 Config Pind.6 = Output Const Pcf8574write = &H40 Const Pcf8574read = &H41 Set Portd.6 Waitms 1000 Reset Portd.6 Waitms 1000 Do Set Portd.6 I2cstart I2cwbyte Pcf8574write I2cwbyte 255 I2cstop Waitms 500 Reset Portd.6 I2cstart I2cwbyte Pcf8574write I2cwbyte 0 I2cstop Waitms 500 Loop EndДве ножки порта D зарезервированы под I2C. I2Cdelay задает скорость шины в 200кГц. Pind.6 настроен на выход, к нему подключен светодиод.
Скомпилируйте код и загрузите в чип. Теперь светодиоды моргают как на контроллере, так и на расширителе портов. Но заметьте, что мигают они в противофазе, это потому диод на расширителе подключен катодом к выходу. Когда на выходе 1, то диод с обеих сторон видит напряжение питание, и гореть он не может. Когда на выходе 0, то ток течет через светодиод в лапку расширителя, и диод светится.
Если вы хотите использовать выводы PCF8574 на вход, вам нужно установить на них высокий уровень, записав 1. Затем, используя кнопку, дергать их на землю (и читать с них 0) или не дергать (и соответственно, читать 1). Но это мы рассмотрим позже.
Скорость I2C
Bascom использует стандартную частоту обмена для I2C в 200 кГц. С помощью параметра I2CDelay можно задать и другую.
Config I2Cdelay = 10Этот код задаст частоту в 100 кГц. Используйте 5 для 200 кГц (по умолчанию). Соответственно, 2 задаст частоту в 500 кГц, а 1 в 1 МГц. Используйте значения 10 или более для низкоскоростных приложений. Иногда при использовании длинных проводов (большая емкость!) нужно понизить скорость обмена, чтобы избежать ошибок.
С другой стороны, большинство микросхем с I2C могут работать на гораздо больших скоростях, чем указано в даташите. Но нет гарантии, что они будут работать в таком режиме при любых условиях, так что несколько раз проверьте работу устройства.
Использование I2cinit
Лапки AVR, используемые с I2C, конфигурируются словами Config SDA/SCL. После подачи питания на контроллер, на них установится логический ноль. Но после команды Portx или после изменения направления input/output, на них могут оказаться непредсказуемые уровни. Поэтому нужно написать:
I2cinitчтобы насильно установить на них низкий уровень перед началом или продолжением I2C обмена.
Считываем состояние выключателя через PCF8574
Соберем следующую схему:

И зальем в наш контроллер следующую программу:
i2c-pcf8574-switch.bas
$regfile = "2313def.dat" $crystal = 4000000 Config Sda = Portd.5 Config Scl = Portd.4 Config I2cdelay = 10 Config Pind.6 = Output Const Pcf8574write = &H40 Const Pcf8574read = &H41 Const Shortwait = 50 Const Longwait = 250 Const Switchbit = 0 Dim Ledwait As Byte Dim Pcf8574port As Byte 'Поместим на все ножки PCF8574 единицу и будем проверять I2cstart I2cwbyte Pcf8574write I2cwbyte 255 I2cstop Do 'Read Pcf8574 port I2cstart I2cwbyte Pcf8574read I2crbyte Pcf8574port , Nack I2cstop 'Determine state of Switchbit bit If Pcf8574port.switchbit = 1 Then Ledwait = Shortwait Else Ledwait = Longwait End If Set Portd.6 Waitms Ledwait Reset Portd.6 Waitms Ledwait Loop EndКонстанты определяют длинную и короткую продолжительности вспышки светодиода.
Константа SwitchBit определяет состояние выключателя на Pin 0 (bit 0) PCF8574.
Две переменные типа Byte задают продолжительность вспышки светодиода и состояние порта PCF8574.
Программа начинается с установки всех ножек порта PCF8574 в высокий уровень, так что их можно использовать как входы.
В цикле Do Loop состояние порта PCF8574 определяется так: сначала отсылается I2cstart, затем PCF8574 читает адрес, а затем I2crbyte используется для получения текущего значения порта PCF8574. I2cstop прекращает обмен.
Скомпилируйте программу и отправьте в контроллер. Проследите за её работой - теперь, когда выключатель разомкнут, светодиод моргает часто, если замкнут - редко.
Использование прерываний PCF8574
Программа, описанная выше, достаточно глупая. Время опроса выключателя зависит от состояния переменной Ledwait (пока отрабатывается задержка, контроллер больше ничего не делает). Это плохой пример программирования. Но мы с вами умные и воспользуемся прерываниями от PCF8574. Если вы еще не знаете, что это такое, то срочно прочитайте статью о прерываниях перед тем, как мы продолжим.
Как было сказано ранее, PCF8574 будет генерировать сигнал прерывания (низкий уровень) каждый раз, когда поменяется состояние на его входах.
Соберем такую схему:

И запишем такую программу:
i2c-pcf8574-switch-int.bas
$regfile = "2313def.dat" $crystal = 4000000 Config Sda = Portd.5 Config Scl = Portd.4 Config I2cdelay = 10 Config Pind.6 = Output Config Pind.2 = Input Config Int0 = Falling Const Pcf8574write = &H40 Const Pcf8574read = &H41 Const Shortwait = 50 Const Longwait = 250 Const Switchbit = 0 Dim Ledwait As Byte Dim Pcf8574port As Byte Ledwait = Shortwait On Int0 Pcfint Enable Interrupts Enable Int0 I2cstart I2cwbyte Pcf8574write I2cwbyte 255 I2cstop Do Set Portd.6 Waitms Ledwait Reset Portd.6 Waitms Ledwait Loop 'PCF8574 interrupt routine Pcfint: 'debounce wait time Waitms 10 'read the input pins I2cstart I2cwbyte Pcf8574read I2crbyte Pcf8574port , Nack I2cstop 'Determine state of Switchbit bit If Pcf8574port.switchbit = 1 Then Ledwait = Shortwait Else Ledwait = Longwait End If Gifr = 64 Return EndТеперь у нас в конфигурации появилась новая строка: Config Int0 = Falling. Это означает, что только спады сигнала на входе Int0 будут вызывать прерывание программы
Также, Int0 на Portd.2 у нас настроен как вход.
В переменную Ledwait изначально кладется значение константы Shortwait.
Когда произойдет прерывание, программа перейдет на метку Pcfint.
Прерывания включены глобально, отдельно включено прерывание Int0.
Программа будет зацикленно моргать светодиодом, пока не случится прерывание. В подпрограмме обработки первым делом выполняется задержка 10 мс, чтобы исключить ложные срабатывания от дребезга контактов. После этого считывается состояние порта PCF8574, а точнее одного бита, помеченного как Switchbit. Если полученное значение - 1, то то переменную Ledwait приравниваем к Shortwait, иначе к Longwait.
Компилируем программу, загружаем в чип. Наблюдаем, что светодиод мигает часто. Замыкаем выключатель и видим, что он стал мигать медленнно. Вместо выключателя можно использовать кнопку.
Обработка данных с оптического энкодера через PCF8574
AVR можно научить читать значения напрямую с энкодера, об этом есть отдельная одноименная глава под номером 18. Но может случиться так, что вам будет не хватать свободных пинов. Тогда на помощь к нам приходит все тот же расширитель PCF8574.
Рассмотрим (значит, соберем на макетной плате!) схему:

И откроем в BASCOM следующую программу:
i2c-pcf8574-int-encoder.bas
$regfile = "2313def.dat" $crystal = 4000000 Config Sda = Portd.5 Config Scl = Portd.4 'default I2C speed Config I2cdelay = 5 Config Pind.6 = Output 'Тут будет светодиод Config Pind.2 = Input 'Прерывание от PCF8574 на вход Int0 Config Int0 = Falling Config Lcd = 16 * 2 Config Lcdpin = Pin , Db4 = Portb.4 , Db5 = Portb.5 , Db6 = Portb.6 , Db7 = Portb.7 , E = Portb.3 , Rs = Portb.2 Config Lcdmode = Port Const Pcf8574write = &H40 Const Pcf8574read = &H41 Const Optencmask = &B00000011 'Энкодер AB подключен к младшим двум битам Const Optencbmask = &B000000001 'Удерживаем B-бит Dim Pcf8574input As Byte Dim Encoderval As Byte Dim Encounter As Integer Dim Oldbval As Byte Encounter = 0 Oldbval = 0 Cls Lcd "encounter:" On Int0 Pcfint 'Настроим расширитель на вход I2cstart I2cwbyte Pcf8574write I2cwbyte Optencmask I2cstop Enable Interrupts Enable Int0 'Сообщим положение энкодера 'Следим за светодиодом на Portd.6 : Если он постоянно включен или выключен, 'то вероятно, срабатывает слишком много прерываний Do Set Portd.6 Waitms 5 Locate 1 , 12 Lcd Encounter ; " " Reset Portd.6 Waitms 5 Loop Pcfint: 'read the input pins I2cstart I2cwbyte Pcf8574read I2crbyte Pcf8574input , Nack I2cstop Encoderval = Pcf8574input And Optencmask If Oldbval = 1 Then Encoderval = Encoderval + 4 End If If Encoderval < 2 Then Decr Encounter Elseif Encoderval > 5 Then Decr Encounter Else Incr Encounter End If Oldbval = Pcf8574input And Optencbmask Return EndConfig Lcdpin используется для задания портов для работы с ЖКИ (в данном случае они совпадают со значениями по умолчанию).
Optencmask используется для установки двух младших битов на энкодере. Optencbmask используется для задания только бита "B".
В основном цикле светодиод на Portd.6 мигает, а на экран выводится положение энкодера. В подпрограмме прерывания считывается байт с I2C порта, из него извлекаются состояния битов на энкодере и определяется его положение, которое обновляется.
Опять же, компилируем и загружаем прошивку в чип. Крутим ручку энкодера и видим, как меняется значение положения на ЖКИ. Если вам достался энкодер с высоким разрешением, вы можете заметить, как светодиод буде включен в течение всего времени, пока вы врашаете ручку. Число генерируемых прерываний настолько велико, что программе не остается времени для возврата в основной цикл. Очень вероятно, что некоторые прерывания будут пропушены, и отображаемое значение не будет меняться так часто, как вам хотелось бы. Вы можете улучшить программу, изменив значение I2cdelay на более низкое, тем самым получив меньшую задержку отклика. Но будьте осторожны при подключении нескольких устройств к одной шине.
Использование ON Semi JLC1562B вместо PCF8574
У JLC1562B нет выхода прерываний, однако есть 6-битный ЦАП, напряжение на нем можно изменять в 64 уровнях, от 0 до 4 вольт, с шагом 0,0625. На входах JLC1562B есть компаратор. На младших 5 битах можно установить порог компаратора ("B") как на половину питающего напряжения, так и на выход ЦАП. Старшие три бита компаратора ("A") всегда настоены на порог в половину питающего напряжения.
По сравнению с PCF8574, в JLC1562B DAC имеет другие режимы чтения и записи. Это нужно знать, чтобы использовать его вместо PCF8574, а также грамотно настроить ЦАП и управлять порогом компаратора. Если используется JLC1562B, как замена PCF8574, последовательность работы с I2C не изменится.
Если вы хотите настроить ЦАП:
- Отправьте JLC1562B адрес записи
- Отправьте байт состояния входов/выходов
- Отправьте 6-битное значение ЦАП (0-63) в младшие 6 бит
- В двух старших битах шестой задает порог компаратора: 0 задает порог в половину питающего напряжения,
1 задает порог значением выхода АЦП, 7 бит управляет "защелкой",
0 защелкивает данные после команды чтения,
1 защелкивает данные, когда компаратор B переключается из 0 в 1
Чтобы использовать ЦАП, соберите следующую схему:

И наберите следующую программу:
i2c-jlc1562b-dac.bas
$regfile = "2313def.dat" $crystal = 4000000 Config Sda = Portd.5 Config Scl = Portd.4 Config I2cdelay = 10 Config Pind.6 = Output Const Jcl1562bwrite = &H70 Dim Dacval As Byte Do Set Portd.6 For Dacval = 0 To 63 I2cstart I2cwbyte Jcl1562bwrite I2cwbyte 0 I2cwbyte Dacval I2cstop Next Dacval Reset Portd.6 Loop EndАдрес для записи в JLC1562B установлен в 70 (в шестнадцатеричной системе). Сверяйтесь с даташитом!
В цикле Do Loop включается светодиод, а в JLC1562B записывается 0 в первый байт, а во второй пишется значение счетчика от 0 до 63 для ЦАП. В этом втором байте старшие биты (порог компаратора и защелкивание) остаются равными 0.
Ловкими и отточенными движениями отправляем скомпилированную прошивку в контроллер. Наблюдаем на выходе ЦАП (13 нога) пилообразный сигнал:

JLC1562B в роли 6-битного АЦП
Вы можете настроить компаратор JLC1562 так, чтобы он использовал в качестве источника опорного напряжения выход ЦАП. Если мы установим выход ЦАП, скажем, в 2 вольта и приложим чуть меньшее напряжение к ногам D0..D4, то считанное значение будет равно 0. Если чуть поднять напряжение, то считываться будет уже 1. Так что любой из выводов D0..D4 можно заставить исполнять функции "сыроватого" 6-битного АЦП, если изменять напряжение на ЦАП за 64 шага и считывать значение компаратора после каждого шага.
Соберём следующую схему:

И, как обычно, наберем программу:
i2c-jlc1562b-adc.bas
$regfile = "2313def.dat" $crystal = 4000000 Config Sda = Portd.5 Config Scl = Portd.4 Config I2cdelay = 10 Config Pind.6 = Output Const Jcl1562bwrite = &H70 Const Jcl1562bread = &H71 'mask out all but the lower six dac bits Const Jcl1562bcompon = &B11000000 'make this pin high to use it as input Const Jclmask = &B00000001 Dim Dacval As Byte Dim Dacwrite As Byte Dim Jclinput As Byte Cls Do Reset Portd.6 For Dacval = 0 To 63 Step 1 Dacwrite = Dacval Or Jcl1562bcompon I2cstart I2cwbyte Jcl1562bwrite I2cwbyte Jclmask I2cwbyte Dacwrite I2cstop I2cstart I2cwbyte Jcl1562bread I2crbyte Jclinput , Nack I2cstop Jclinput = Jclinput And Jclmask If Jclinput = Jclmask Then Exit For End If Next Dacval Locate 1 , 1 Lcd Dacval ; " " Loop EndВ данном примере производится операция ИЛИ: Dacwrite = Dacval Or Jcl1562bcompon, так что оба бита - бит выбора источника опорного напряжения для компаратора B и бит защелки - устанавливаются в 1.
Jlcmask используется, чтобы установить бит 0 на порту JLC1562B в единицу, чтобы он работал как вход.
В цикле Do Loop включается светодиод, в течение следующих шагов значение Dacval возрастает от 0 до своего максимума 63 (0..63 * 0.0625 = приблизительно 4 вольта). После каждого увеличения считывается значение компаратора. Если при этом оно изменилось (стало 1), считается что компаратор сработал. Тогда цикл прерывается, а значение Dacval выводится на ЖКИ.
АЦП/ЦАП Philips PCF8591
PCF8591 имеет на борту один 8-битный ЦАП и четыре 8-битных АЦП. Чтобы они заработали, вывод EXT нужно подключить к земле. Тогда на выводе OSC появится нечто, похожее на тактовые импульсы 1 МГц:

Вы можете также использовать внешний тактовый генератор (0.75 - 1.25 МГц), подключив его к выводу OSC. Тогда вывод чипа EXT нужно подключить к плюсу питания.
PCF8591 управляется тремя незамысловатыми байтами:
- байт адреса
- байт команды
- байт значения ЦАП
Байт управление выглядит следующим образом:
7 6 5 4 3 2 1 0 (позиция бита)
0 a p p 0 i c c (управляющие биты)
Где:
a = 1 для включения аналогового выхода (но также нужно включить, если планируется использование АЦП)
pp = конфигурация АЦП, нужно выставить 00 для 4 независимых входов A0, A1, A2, A3. Другие значения изучите в даташите самостоятельно!
i = 1 для включения автоувеличения каналов АЦП. Увеличивает значение сс после каждого чтения.
cc = номер канала АЦП, 0, 1, 2 или 3.
Соберём следующую схему:

Для проверки ЦАП введём следующую программу:
i2c-pcf8591-dac-triangle.bas
$regfile = "2313def.dat" $crystal = 4000000 Config Sda = Portd.5 Config Scl = Portd.4 Config I2cdelay = 10 Config Pind.6 = Output Const Pcf8591write = &H90 Const Pcf8591read = &H91 Const Pcf8591dacconfig = &B01000000 ' | ' -------- включаем аналоговый выход ' (также нужно и для АЦП) Dim Dacout As Byte Set Portd.6 Waitms 1000 Reset Portd.6 Waitms 1000 Do Set Portd.6 For Dacout = 1 To 255 Step 1 I2cstart I2cwbyte Pcf8591write I2cwbyte Pcf8591dacconfig I2cwbyte Dacout I2cstop Next Dacout Reset Portd.6 For Dacout = 255 To 1 Step -1 I2cstart I2cwbyte Pcf8591write I2cwbyte Pcf8591dacconfig I2cwbyte Dacout I2cstop Next Dacout Loop EndВ константе Pcf8591dacconfig установлен 6 бит, включающий аналоговый выход.
В цикле Do Loop, For Next цикл меняет значение Dacout от 1 до 255 и отсылает Dacout как третий управляющий байт.
Затем For Next цикл меняет значение Dacout в обратном порядке, от 255 до 1.
На выводе 15 (Aout) можно наблюдать треугольный сигнал:

Изменение Config I2cdelay с 10 до 1 ускоряет процесс:

Конечно, все эти наши приколы ограничены частотой в 100 КГц, это максимальная частота, поддерживаемая PCF8591.
Но тем не менее, программу можно переделать, чтобы получать более высокие частоты:
i2c-pcf8591-triangle-faster.bas
$regfile = "2313def.dat" $crystal = 4000000 Config Sda = Portd.5 Config Scl = Portd.4 Config I2cdelay = 10 Config Pind.6 = Output Const Pcf8591write = &H90 Const Pcf8591read = &H91 Const Pcf8591dacconfig = &B01000000 Dim Dacout As Byte Set Portd.6 Waitms 1000 Reset Portd.6 Waitms 1000 I2cstart I2cwbyte Pcf8591write I2cwbyte Pcf8591dacconfig Do For Dacout = 1 To 255 Step 1 I2cwbyte Dacout Next Dacout For Dacout = 255 To 1 Step -1 I2cwbyte Dacout Next Dacout Loop EndТеперь первые два управляющий байта отсылаются перед циклом Do Loop.
В самом цикле лишь происходит запись значения Dacout в чип PCF8591.
Обратите внимание: в этой программе нет команды I2cstop! В реальной же программе такой поворот событий маловероятен, потому что наверняка будут происходить какие то другие процессы с шиной I2C.
Посмотрим на выходной сигнал теперь:

Но мы опять ускорим программу, заменив, как и обешано, I2cdelay на 1:

И по идее можно добиться ещё большего ускорения, установив в схему кварц на 10 МГц, благополучно не сказав об этом Bascom. Тогда результат будет таким:

Разумеется, грамотный читатель никогда не будет делать таких ужасных вещей при разработке коммерческих продуктов. Но домашнее применение подразумевает попирание всех правил, но и подразумевает возможные последствия.
Температурный сенсор National Semiconductor LM76
LM76 - это одна из кучи микросхем для измерения температуры, с I2C интерфейсом. У большинства крупных производителей такой чип непременно есть в "портфолио". LM76 не предполагает высокой точности, и поэтому позорно называется "оценщик температуры". На самом деле она дает точность измерений около одного градуса Цельсия. В общем, почитайте даташит на досуге.
В этом чипе 6 регистров. Регистр, из которого нужно читать температуру, выбирается по умолчанию после включения питания. Подробнее об этих регистрах я расскажу позже. LM76 меряет и запоминает температуру с точностью 0.0625oC. Температура хранится как 16-битное слово, считывается как два байта:

D0-D2 не определены. D15 - бит знака, само значение температуры лежит в D3-D14. Температура хранится в обычном двоичном представлении. Это значит, что положительные числа хранятся как есть, с битом знака равным 0. Отрицательные числа хранят 1 в бите знака, сам модуль числа записан в инвертированном виде и увеличен на 1. Если вы предполагаете использовать датчик для измерения только плюсовых температур, забудьте про эти махинации со знаками и просто считывайте биты D3-D14. Однако если вы из мест не столь отдаленных, вас наверняка заинтересует метод считывания отрицательных значений. Для этого я приготовил вам следующий код:
... Dim Tempint as Word Dim Tempbytelo as Byte Dim Tempbytehi as Byte ... 'считаем два байта из lm76. Это просто, как два байта переслать =) ... Tempint = Makeint(Tempbytelo, Tempbytehi) Tempsign = Tempbytehi And &B1000000 If Tempsign = &B10000000 Then Tempint = Not Tempint Tempint = Tempint + 1 End If Shift Tempint, Right, 3 ...Makeint объединяет принятые два байта в одно 16-битное слово и кладет в переменную Tempint. Затем, путем откусывания самого старшего (левого) бита, извлекается знак числа. Если бит равен 1, нам нужно инвертировать все биты в Tempint (убрав тем самым бит знака) и увеличить результат на 1. Наконец, все биты в Tempint нужно сдвинуть вправо на 3 разряда, убрав тем самым бесполезные биты D0-D2. Теперь в Tempint хранится модуль температуры с точностью 0.0625oC, а в Tempsign лежит знак температуры. Все проще некуда.
Посему соберем следующую интересную схему:

И введём следующую интересную программу:
i2c-lm76.bas
$regfile = "2313def.dat" $crystal = 4000000 Config Sda = Portd.5 Config Scl = Portd.4 Config I2cdelay = 100 Config Pind.6 = Output Const Lm76write = &H90 Const Lm76read = &H91 Dim Tempint As Integer At $80 Dim Tempbytelo As Byte At $80 Overlay Dim Tempbytehi As Byte At $81 Overlay Dim Templong As Long Cls Do Set Portd.6 I2cstart I2cwbyte Lm76write 'указатель на регистр температуры. 'хоть и установлен по умолчанию, он здесь просто для примера. I2cwbyte 0 I2cstart I2cwbyte Lm76read I2crbyte Tempbytehi , Ack I2crbyte Tempbytelo , Nack I2cstop Cls Lcd Tempbytehi ; " " ; Tempbytelo Lowerline Shift Tempint , Right , 3 Templong = Tempint * 65 Lcd Tempint ; " " ; Templong Reset Portd.0.6 Wait 1 Loop EndВ этом примере игнорируется знак температуры. Поэтому нужно привести еще один, более продвинутый пример, который будет показывать нам температуру сразу в градусах Цельсия:
i2c-lm76-fusing.bas
$regfile = "2313def.dat" $crystal = 4000000 Config Sda = Portd.5 Config Scl = Portd.4 Config I2cdelay = 100 Config Pind.6 = Output Const Lm76write = &H90 Const Lm76read = &H91 Const Lm76resolution = 0.0625 Dim Tempint As Word Dim Tempbytelo As Byte Dim Tempbytehi As Byte Dim Temperature As Single Dim Tempstring As String * 4 Dim Tempsign As Byte Dim Flashnumber As Byte Dim Flashloop As Byte Dim Flashtime As Word Do Set Portd.6 Wait 1 Cls I2cstart I2cwbyte Lm76read I2crbyte Tempbytehi , Ack I2crbyte Tempbytelo , Nack I2cstop Lcd Tempbytehi ; " " ; Tempbytelo ; " " Tempint = Makeint(tempbytelo , Tempbytehi) Tempsign = Tempbytehi And 128 If Tempsign = 128 Then Tempint = Not Tempint Tempint = Tempint + 1 End If Shift Tempint , Right , 3 Lcd Tempint ; " " Temperature = Tempint * Lm76resolution Lowerline Lcd Temperature ; " " Tempstring = Fusing(temperature , "##.#") If Tempsign = 128 Then Lcd "-" Lcd Tempstring Reset Portd.6 Wait 1 Loop EndЭта программа использует операции с плавающей точкой. Для этого подгружаются специальные библиотеки. Хоть программа и очень маленькая, она займет 99% памяти в вашем AT90S2313 (а в ATTINY2313 даже не влезет)!
При работе с маленькими контроллерами следует избегать операций с плавающими точками, где это возможно. Следующая программа покажет, как сделать это, используя переменные типа Integer. Она также будет работать намного быстрее:
i2c-lm76-usingintegers.bas
$regfile = "2313def.dat" $crystal = 4000000 Config Sda = Portd.5 Config Scl = Portd.4 Config I2cdelay = 100 Config Pind.6 = Output Const Lm76write = &H90 Const Lm76read = &H91 Const Lm76resolution = 625 Dim Tempint As Word Dim Tempbytelo As Byte Dim Tempbytehi As Byte Dim Tempall As Long Dim Temprem As Long Dim Temptemp As Long Dim Tempdeg As Long Dim Tempsign As Byte Dim Tempdigit As Integer Do Cls Set Portd.6 I2cstart I2cwbyte Lm76read I2crbyte Tempbytehi , Ack I2crbyte Tempbytelo , Nack I2cstop Lcd Tempbytehi ; Tempbytelo ; " " Tempint = Makeint(tempbytelo , Tempbytehi) Tempsign = Tempbytehi And 128 If Tempsign = 128 Then Tempint = Not Tempint Tempint = Tempint + 1 End If Shift Tempint , Right , 3 Lcd Tempint ; " " Tempall = Tempint * Lm76resolution Lcd Tempall ; " " Tempdeg = Tempall / 10000 Temptemp = Tempdeg * 10000 Temprem = Tempall - Temptemp Lowerline Lcd Tempdeg ; " " ; Temprem ; " " Tempdigit = Temprem / 1000 Temptemp = Tempdigit * 1000 Temprem = Temprem - Temptemp If Temprem > 499 Then Tempdigit = Tempdigit + 1 If Tempdigit > 9 Then Tempdigit = 0 Tempdeg = Tempdeg + 1 End If If Tempsign = 128 Then Lcd "-" Lcd Tempdeg ; "." ; Tempdigit Loop EndВ этой программе больше строк, но, как ни странно, она занимает всего 85% в памяти контроллера, оставляя место для некоторых других функций.
Управление графическим дисплеем Philips LPH7653
Дисплей Philips LPH7653 вероятно был разработан для мобильных телефонов ранних поколений. Он имеет разрешение 97x35 пикселей.
Информацию по нему можно найти тут.
Прочитайте это описание (хоть оно и на английском, просто посмотрите картинки)! Наверняка вам придется подбирать напряжения питания и регулировки контрастности, чтобы суметь что-то прочитать на нем. Светодиодную подсветку можно питать стандартным напряжением +5 вольт.
LP7653 - чисто графический дисплей. Он не имеет знакогенератора, это значит, что вам придется самим рисовать символы, если вы хотите выводить текст. Но это не так сложно, как может показаться.
Дисплей имеет ширину в 97 пикселей, однако судя по всему, видеопамять у него рассчитана на 101 пиксель. Каждая строка из 8 пикселей имеет индивидуальную адресацию:

В программном плане это выглядит так:
I2cstart I2cwbyte lph7653writeaddress '(7Ahex) I2cwbyte linenumber '(60, 61, 62, 63 or 64hex для строк 0, 1, 2, 3 и 4) I2cwbyte pixelcolumn '(00...60hex для столбцов 0...97) I2cwbyte pixelbyte '(младший бит - сверху) I2cwbyte pixelbyte '(pixelcolumn/linenumber идёт pixelcolumn 64hex) I2cwbyte ... I2cstopДля очистки экрана вы можете записать 5x101 нулевых байтов начиная с первой строки и первого столбца. Если вы планируете использовать дисплей только для вывода текста, разумно было бы использовать его как четырёхстрочный. Тогда вы можете использовать символы высотой в 8 пикселей. Вы можете выводить стандартные символы 7x5 как символы 7x6. Таким образом, нижний ряд пикселей будет пустым, создавая отступ между строками текста. Пятая строка доступна для вывода, но отображаться будут только верхние три ряда пикселей (35 - (4 x 8)).
Для проверки соберём следующую схему:

И введём следующую программу:
i2c-lph7653-75.bas для символов 7x5
или i2c-lph7653-75prop.bas для символов 7x6
(листинги здесь не приводятся, так как там много скучных и неинтересных строк)
Программа i2c-lph7653-76prop.bas использует символы 7x6, поэтому каждая строка Data хранит 6 байтов.


Если вы хотите использовать дисплей для вывода графики, вам нужно представить изображение в виде 8-битных столбцов пикселей. Это немного отличается от метода вывода на дисплей Toshiba T6963, для которого в Bascom есть утилита конвертирования изображений.
Если вы просто хотите "слить" изображение на дисплей, лучше всего привести его к разрешению 101x35 и выводить видимую часть 97x32 начиная с левого верхнего края. Конечно, вы можете выводить и меньшие изображения, но тогда внимательно следите за тем, как меняется адресация.
Для преобразования изображений в блок данных есть небольшая утилита на Delphi. Она считывает черно-белое изображение BMP и выводит в виде текста, который вы можете скопировать и вставить в программу. Исходные коды прилагаются!
Вот вам пример работы:










Загружаем первый битмап в утилиту и видим следующее:

Вы можете загрузить и переконвертировать все изображения по очереди, а затем вставить полученный код в нужное место вашей программы на Bascom.
Управление синтезатором частоты Linear Technology LT6904
LT6904 - это чип от Linear Technology, выполняющий функцию синтезатора частоты. Его собрат LT6903 имеет SPI интерфейс, но LT6904 использует I2C. Еще есть чипы LT1799, LT6900, LT6902 и LT6905, в которых частота задается внешним переменным резистором.
LT6904 управляется следующим путем (согласно даташиту):



где OCT - четырёхбитное и DAC - десятибитные слова. Также еще два бита применяются для конфигурирования выходов. Убедитесь, что вы отключили неиспользуемый выход (читайте даташит!) В итоге мы отсылаем 16 бит в таком порядке:
Oct3, Oct2, Oct1, Oct0, DAC9, DAC8...DAC0, Cfg1, Cfg0.
Соберём такую схему:

Обратите внимание, даташит утверждает, что напряжение питание может быть от 2.7 до 5.5 вольт. Однако была замечена неприятная вещь - при питании 5 вольт, выход сигнала LT6904 "пропадает". Такая особенность поведения пропала после понижения питания до 3.3 вольта.
Загрузим для самостоятельного изучения следующую программу:
i2c-lt6904.bas Автор оригинала не предоставил код. Пусть это будет на его совести!
У нас возникает небольшая проблема: Bascom говорит, что программа займет 162% программной памяти. Очевидно, что эта программа жирновата для AT90S2313. Пора обратиться к более мощным контроллерам. Об этом будет рассказано в следующих главах.