Естественно, с полной поддержкой всего этого дела.
читать дальшеРабота с модулем, в целом, такая же, как и в других контроллерах: даёшь ему команды, он их выполняет и отчитывается о результате:
Я> Шли START.
S> Ок, послал.
Я> Круто, шли адрес теперь. Вот такой: 0xXX.
S> Ок, послал. Мне сказали, что ACK. Давай дальше.
Я> Жив ещё, хорошо. Вот тебе номер регистра: 0xYY, - шли.
S> Послал, получил ACK.
Я> Шли ему теперь данные, вот тебе байт: 0xZZ.
S> Послал, он согласен на большее: ACK.
Я> Фиг ему, а не ещё. Шли STOP.
S> Okay.
И всё примерно в таком духе.
В данном контроллере выводы i2c раскиданы по портам таким образом:
PB6: I2C1_SCL
PB7: I2C1_SDA
PB8: I2C1_SCL
PB9: I2C1_SDA
PB10: I2C2_SCL
PB11: I2C2_SDA
PA8: I2C3_SCL
PC9: I2C3_SDA
Вообще, распиновку периферии удобно смотреть в даташите на 59 странице.
Что удивительно, но для работы с i2c нужны все его регистры, благо их немного:
I2C_CR1 — команды модулю для отправки команд/состояний и выбор режимов работы;
I2C_CR2 — настройка DMA и указание рабочей частоты модуля (2-42 МГц);
I2C_OAR1 — настройка адреса устройства (для slave), размер адреса (7 или 10 бит);
I2C_OAR2 — настройка адреса устройства (если адресов два);
I2C_DR — регистр данных;
I2C_SR1 — регистр состояния модуля;
I2C_SR2 — регистр статуса (slave, должен читаться, если установлен флаги ADDR или STOPF в SR1);
I2C_CCR — настройка скорости интерфейса;
I2C_TRISE — настройка таймингов фронтов.
Впрочем, половина из них типа «записать и забыть».
На плате STM32F4-Discovery уже есть I2C устройство, с коим можно попрактиковаться: CS43L22, аудиоЦАП. Он подключён к выводам PB6/PB9. Главное, не забыть подать высокий уровень на вывод PD4 (там сидит ~RESET), иначе ЦАП не станет отвечать.
Порядок настройки примерно таков:
1. Разрешить тактирование портов и самого модуля.
Нам нужны выводы PB6/PB9, потому надо установить бит 1 (GPIOBEN) в регистре RCC_AHB1ENR, чтоб порт завёлся.
И установить бит 21 (I2C1EN) в регистре RCC_APB1ENR, чтоб включить модуль I2C. Для второго и третьего модуля номера битов 22 и 23 соответственно.
2. Дальше настраиваются выводы: выход Oped Drain (GPIO->OTYPER), режим альтернативной функции (GPIO->MODER), и номер альтренативной функции (GPIO->AFR).
По желанию можно настроить подтяжку (GPIO->PUPDR), если её нет на плате (а подтяжка к питанию обеих линий необходима в любом виде). Номер для I2C всегда один и тот же: 4. Приятно, что для каждого типа периферии заведён отдельный номер.
3. Указывается текущая частота тактирования периферии Fpclk1 (выраженная в МГц) в регистре CR2. Я так понял, это нужно для расчёта разных таймингов протокола.
Кстати, она должна быть не менее двух для обычного режима и не менее четырёх для быстрого. А если нужна полная скорость в 400 кГц, то она ещё и должна делиться на 10 (10, 20, 30, 40 МГц).
Максимально разрешённая частота тактирования: 42 МГц.
4. Настраивается скорость интерфейса в регистре CCR, выбирается режим (обычный/быстрый).
Cмысл таков: Tsck = CCR * 2 * Tpckl1, т.е. период SCK пропорционален CCR (для быстрого режима всё несколько хитрее, но в RM расписано).
5. Настраивается максимальное время нарастания фронта в регистре TRISE. Для стандартного режима это время 1 мкс. В регистр надо записать количество тактов шины, укладывающихся в это время, плюс один:
если такт Tpclk1 длится 125 нс, то записываем (1000 нс / 125 нс) + 1 = 8 + 1 = 9.
6. По желанию разрешается генерация сигналов прерывания (ошибки, состояние и данных);
7. Модуль включается: флаг PE в регистре CR1 переводится в 1.
Дальше модуль работает уже как надо. Надо только реализовать правильный порядок команд и проверки результатов. Например, запись регистра:
1. Сначала нужно отправить START, установив флаг с таким именем в регистре CR1. Если всё ок, то спустя некоторое время выставится флаг SB в регистре SR1.
Хочу заметить один момент, - если нет подтяжки на линии (и они в 0), то этот флаг можно не дождаться вовсе.
2. Если флаг-таки дождались, то отправляем адрес. Для семибитного адреса просто записываем его в DR прям в таком виде, как он будет на линии (7 бит адреса + бит направления). Для десятибитного более сложный алгоритм.
Если устройство ответит на адрес ACK'ом, то в регистре SR1 появится флаг ADDR. Если нет, то флаг AF (Acknowledge failure).
Если ADDR появился, надо прочитать регистр SR2. Можно ничего там и не смотреть, просто последовательное чтение SR1 и SR2 сбрасывает этот флаг. А пока флаг установлен, SCL удерживается мастером в низком состоянии, что полезно, если надо попросить удалённое устройство подождать с отправкой данных.
Если всё ок, то дальше модуль перейдёт в режим приёма или передачи данных в зависимости от младшего бита отправленного адреса. Для записи он должен быть нулём, для чтения - единицей.
но мы рассматриваем запись, потому примем, что там был ноль.
3. Дальше отправляем адрес регистра, который нас интересует. Точно так же, записав его в DR. После передачи выставится флаг TXE (буфер передачи пуст) и BTF (передача завершена).
4. Дальше идут данные, которые можно отправлять, пока устройство отвечает ACK. Если ответом будет NACK, то эти флаги не установятся.
5. По завершении передачи (или в случае непредвиденного состояния) отправляем STOP: устанавливается одноимённый флаг в регистре CR1.
При чтении всё то же самое. Меняется только после записи адреса регистра.
Вместо записи данных идёт повторная отправка START (повторный старт) и отправка адреса с установленным младшим битом (признак чтения).
Модуль будет ждать данных от устройства. Чтобы поощрать его к отправке следующих байт, надо перед приёмом установить флаг ACK в CR1 (чтобы после приёма модуль посылал этот самый ACK).
Как надоест, флаг снимаем, устройство увидит NACK и замолчит. После чего шлём STOP обычным порядком и радуемся принятым данным.
Вот то же самое в виде кода:
Конечно, кроме как в учебном примере так делать нельзя. Ожидание окончания действия слишком уж долгое для такого быстрого контроллера.
UPD от 29.03.2013: вместо внешнего редкого i2c-устройства max44005 для примера взят имеющийся на плате ЦАП.
Файлы:
i2c.c
Проект.
<< Предыдущее Следующее >>
@темы: arm, программизмы, электроника, stm32f4discovery, stm32