PIC мк. Эксперимент №13. Программный I2C.

Следующим экспериментом в серии выступает применение протокола передачи I2C. Здесь нет описания самого протокола, этого добра в сети хватает :) в статье содержится инструкция по установке связи между микроконтроллером PIC16f628a и так называемым “расширителем” портов мк PCA9539.

Итак:

Задача: Установка связи по протоколу I2C.

Исходный материал: PIC16f628a, PCA9539, devboard.

Вкратце о том, зачем это мне: у меня сейчас на стадии разработки проект часы/термометр, и я выбирал средство индикации для него. Остановился на двух дисплеях: трехразрядный семисегментник и четырехразрядный семисегментник. Для управления ими нужно 8+4+8+3 = 23 бита с нашими 16 портами это нереально. Сначала я думал использовать для этого сдвиговый регистр, но потом мне прилетели халявные образцы от NXP и я решил заюзать их. В данном случае будет использоваться 7+2 = 9 портов, причем 2 порта для шины I2C будут использоваться и для связи с термометром и часами реального времени.

Для начала посмотрим внутрь нашего io expander’a:

pca

Помимо 16 портов здесь присутствует два бита ручного указания адреса, а также вывод прерываний. Фактически, адрес конкретно устройства здесь формируется только двумя битами А0 и А1:

adres

В моем случае я эти биты подтянул на землю, то есть адрес устройства выглядит так:

adr1

То есть начало передачи для нашей индикации должно начинаться с такого байта.

Далее должен следовать командный байт, который указывает куда именно собираемся записывать информацию (конфигурационный бит, непосредственно порты и т.д.).

*Цифры ниже приведены в десятичной системе

0 Входной порт 0
1 Входной порт 1
2 Выходной порт 0
3 Выходной порт 1
4 Инверсия полярности порта 0
5 Инверсия полярности порта 1
6 Конфигурационный порт 1
7 Конфигурационный порт 2

При работе с данной микросхемой нужно помнить, что регистры всегда работают в паре, то есть если вы пишите в порт 0 данные, то придется их записывать и порт 1, командный бит по сути просто указывает на то, в какой из портов данные будут записываться сначала.

После командного байта записываются два нужных нам байта. В общем случае запись в выходные порты выглядит примерно так:

i2cwr

Остановимся пока на только на записи в микросхему, с чтением будем разбираться чуть позже. Переходим непосредственно к коду. Здесь главная фишка в том, что мы оперируем не непосредственно состояниями выходов порта, а лишь изменением направления данного порта (ввод/вывод). Поэтому не забываем вначале нашей программы добавлять следующие строки:


#define SCL TRISB6

#define SDA TRISB5

#define SCL_IN RB6

#define SDA_IN RB5

Задержка для тактирования:


void i2c_delay (void)

{

__delay_us(10);

}

Далее процедура старта:


void i2c_start(void)  //

{

SCL = 1;

SDA = 1;

i2c_delay();

SDA = 0;

i2c_delay();

SCL = 0;

}

Процедура окончания:


void i2c_stop (void)

{

SDA = 0;

i2c_delay();

SCL = 1;

i2c_delay();

SDA = 1;

i2c_delay();

}

Процедура записи выглядит так:


bit i2c_tx(unsigned char d)

{

char x;

static bit b;

             for (x=0; x<8; x++) {    //передача 8 бит данных
                             i2c_delay();
                             if (d&0x80) SDA = 1;
                             else SDA = 0;
                             i2c_delay();
                             SCL = 1;
                             d <<= 1;
                             i2c_delay();
                             SCL = 0;   }
i2c_delay();
i2c_delay();
SDA = 1;              //готовимся получить ACK бит
SCL = 1;
i2c_delay();
b = SDA_IN;    //получаем ACK бит
SCL = 0;
return b;           //Возвращаем значение бита ACK через функцию
}

Итак, для записи в микросхему у нас есть все что нужно. В качестве примера будем выводить двухзначное число на дисплей, которое будет увеличиваться на единицу по нажатию на кнопку. Для вывода на дисплей создадим отдельную функцию:


void dispo(unsigned char temp)

{
unsigned char i = 0;
i = trunc(temp/10);
maj= 0; min =0;
i2c_start();
i2c_tx(0b11101000);
i2c_tx(0b00000010);
i2c_tx(digit[i]);
i2c_tx(digit[i]);
i2c_stop();
maj= 1; min = 0;
__delay_ms(5);
i = temp - (trunc(temp/10)*10);
maj= 0; min = 0;
i2c_start();
i2c_tx(0b11101000);
i2c_tx(0b00000010);
i2c_tx(digit[i]);
i2c_tx(digit[i]);
i2c_stop();
maj= 0; min = 1;
__delay_ms(5);
}

Надо также помнить, что перед записью в порты их нужно сконфигурировать на выход, записав нули в конфигурационные биты. Нажатие на кнопку обрабатывается прерыванием. Результат работы:

Чтение будет рассмотрено в другой статье и из другого девайса (как только он прилетит из китая).

Исходники здесь.

PIC мк. Эксперимент №13. Программный I2C.: 12 комментариев

  1. Уведомление: PIC мк. Эксперимент №17. Часы-термометр. « PIC микроконтроллеры

  2. …………перед записью в порты их нужно сконфигурировать на выход……..

    i2c_start();
    i2c_tx(0b11101000); //Address
    i2c_tx(0b00000110); //command 00000110=6 ->Конфигурационный порт 1
    i2c_tx(0b00000000);
    i2c_tx(0b00000000);
    i2c_stop();

    Незаб(и)вать!

    Ответить

  3. Да, еще забыл добавить, позднее я понял что использование функции trunc() здесь избыточно, как и использование математической либы и + на видео версия софта без защиты от дребезга

    Ответить

  4. А если в ответ на адрес приходит NACK, то это траблы в коде или же в аппаратной части?

    Ответить

    sarge Ответил:

    Оба варианта могут быть верны.
    1. В случае неправильного указания адреса, естественно ничего не придет (проверяйте анализатором)
    2. Косяк соединения, тут вы хоть в воздух две шины подключите nack будет постоянно :)

    Ответить

  5. Косяков в соединении не обнаружил. Прогнал в режиме дебагера, выяснилось, что со стороны слейва линию никто прижимать не собирается. Дошел до чтения, и там у меня насобирались одни лишь единички. Может ли это быть из-за задержек, у меня задержки тоже на 10 мкс(частота камня 20 МГц). Резюки на 10 к. Неужели микруха приказала долго жить?

    Ответить

    sarge Ответил:

    Если есть другая микросхема, можно попробовать на ней какую-нибудь простую команду.

    Ну или же код в протеус и посмотреть работает ли там все адекватно.

    PS. С адресом то все в порядке? Может не тот адрес указываете вот она и не реагирует.

    Ответить

    Reaper Ответил:

    У микрухи DS1621 адрес начинается 1001, и еще три адресных входа, которые настраиваешь сам, ну я их и повесил на землю, то есть адрес 1001000(R/W). Если слать на запись, то выглядит i2c_tx(0x90). В протеусе все отлично работает, без вопросов.

    Ответить

    sarge Ответил:

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

    Ответить

    Reaper Ответил:

    Ну тогда завтра так и сделаю, спасибо за помощь.

    Ответить

    sarge Ответил:

    Да не за что.

    Ответить

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Можно использовать следующие HTML-теги и атрибуты: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">