Сегодня я хотел бы кратко рассказать о модуле I2C на микроконтроллере STM8S003F и поделиться своими наработками в этой области.
Признаться честно, долго пришлось повозиться с модулем I2C– он ни в какую не хотел работать так, как нужно, пока я не прочитал errata и не устранил еще некоторые мелкие, но коварные ошибки, которые перекочевали в мою программу из примера, предоставленного на официальном сайте STMicroelectronics.
В этой статье я покажу как настроить I2C в режиме мастера без прерываний. В самом конце статьи можно будет скачать готовый аппартный драйвер I2C для STM8. В следющей статье попробуем подключить часы реального времени DS1307 и выводить время через UART. Возможно, в дальнейшем будет пример на прерываниях, или может кто-нибудь захочет поделиться своими наработками – милости прошу.
Для тех, кто желает разобраться в принципе работы I2C интерфейса, советую почитать краткую и в тоже время, полезную статью: Интерфейсная шина IIC (I2C)
Также советую почитать полезное замечание: Как я побеждал I2C (STM8L)
Еще нужно будет почитать статью по настройке модуля UART и ознакомиться с даташитами STM8S003 и Reference manual.
Рассмотрим основные возможности модуля I2C:
- Поддержка режима Master и Slave(ведущий и ведомый)
- Генерация и определение 7- и 10-битного адресов
- Поддержка разных скоростей передачи:
- Стандартный режим 100 кГц
- Высокоскоростной режим 400 кГц
Как обычно начинаем с инициализации.
1. Задаем частоту тактирования модуля I2C. Это частота Fmaster, которая снимается перед делителем CPUDIV. Если Вы читали прошлую статью по настройке модуля UART, то знаете, что по умолчанию в качестве источника тактирования выбран RC-генератор (16МГц), с делением частоты на 8. Т.е. по умолчанию Fmaster= 2МГц. Значение Fmaster записывается в регистр I2C_FREQR в МГц. Значения, которые могут быть записаны в данный регистр, находятся в диапазоне 1…24 МГц.
2. Дальше нужно решить в каком режиме мы будем работать. DS1307 работает только в стандартном режиме (100 кГц), поэтому его и выбираем. Стандартный режим выбирается сбросом бита F/S в регистре I2C_CCRH.
3. Настраиваем регистры управления тактированием I2C_CCRL и I2C_CCRH. Эти регистры определяют длительность импульса тактировании SCL и длительность паузы.
I2C_CCRL

I2C_CCRH

Коэффициент CСR вычисляется по разным формулам (они описаны в reference manual), в зависимости от выбранного режима работы. Для стандартного режима он вычисляется по формуле:
CCR= Period_I2C/(2*Tmaster), где
Period_I2C– период импульсов SСLна I2C шине, в стандартном режиме минимальный период равен 1/100кГц,
Tmaster– период частоты тактирования периферии, Tmaster= 1/Fmaster= 1/2 000 000 Гц.
Таким образом формула принимает вид:
CCR= (Fmaster* 1 000 000)/(2 * Fi2c), где
Fmaster — частота периферии в МГц,
Fi2c– частота I2Cшины.
Бит DUTY позволяет выбрать коэффициент заполнения для высокоскоростного режима.
4. Запрограммировать максимальное время нарастания сигнала SCL. По спецификации время нарастания сигнала SCL в стандартном режиме не должно превышать 1000 нс. Время нарастания задается в регистре TRISE.
Значение регистра вычисляется по формуле:
TRISE= (1000 нс/Tmaster) + 1.
Если частота Fmaster=2МГц, то период равен 500нс, значит значение регистра:
TRISE = (1000 нс/500нс) + 1 = 3
5. Включаем модуль I2C установкой бита PE=1 в регистре I2C_CR1.
6. Устанавливаем бит, разрешающий подтверждение принятого байта ACK=1 в регистре I2C_CR2
Опишу кратко регистры статуса и управления, с которыми нам придется дальше работать.
I2C_CR2

- SWRST – программный сброс модуля I2C
- POS– этот бит устанавливается только в случае, когда нужно принять два байта (N=2). Он должен быть установлен до начала приема данных. В этом случае импульс NACKсформируется после приема второго байта, и ACK можно сразу же сбросить сразу после отправки адреса.
- ACK – разрешает отсылать подтверждения от мастера после приема каждого байта
- STOP – дает указание мастеру сгенерировать стоповую посылку после приема текущего байта
- START– дает указание мастеру сгенерировать стартовую посылку
I2C_SR1

- TXE – регистр данных DR пуст
- RXNE – в регистре данных DRсодержится принятый байт
- STOPF– обнаружена стоп-посылка на шине
- ADD10 – мастер отправил первый байт 10-битного адреса
- BTF– устанавливается когда в приемный регистр DR поступил байт, но не был прочитан, и уже пришел новый байт в сдвиговый регистр
- ADDR – мастер передал адрес
- SB– мастер сгенерировал старт-посылку
I2C_SR2

- WUFH – выход из спящего режима
- OVR – ошибка переполнения регистра данных
- AF– ошибка подтверждения отправленного мастером байта
- ARLO – потеря арбитража (на шине, где более одного мастера)
- BERR– ошибка шины (например когда послан неуместный старт или стоп)
I2C_SR3

- TRA– если данные приняты = 0, если переданы = 1
- BUSY – сигнализирует о занятости шины
- MSL – показывает в каком режиме находится модуль I2C. 1 – мастер, 0 – слейв
Также есть регистр настройки прерываний I2C_ITR, который мы не будем настраивать в данном примере. Также для слейва имеются регистры, в которые записывается собственный адрес I2C устройства — I2C_OARL, I2C_OARH.
Дальше я немного перевел для вас информации из reference manual. Если где-то ошибся, то прошу меня поправить.
Режим мастера
В режиме мастера I2Cинтерфейс инициирует передачу данных и генерирует clockсигнал тактирования. Каждая передача начинается СТАРТ условием и оканчивается СТОП условием. Режим мастера включается сразу же после генерации СТАРТ.
Входная частота должна быть не меньше:
- 1 MHz в стандартном режиме
- 4 MHz в быстром режиме
Стартовое условие (стартовая посылка)
Установка бита STARTприводит к генерации стартовой посылки и переключает в режим мастера, при условии, что BUSY= 0.
Заметка: В режиме мастера установка START бита генерирует посылку рестарта (повторного старта) в конце текущего передаваемого байта.
Сразу же после стартовой посылки устанавливает бит SB и генерируется прерывание, если оно разрешено ITEVTEN= 1.
Далее мастер ожидает чтение регистра SR1, после чего в регистр DR записывается адрес слейв-устройтва (slaveaddress).
Передача слейв-адреса
Cлейв-адрес передается в SDA линию из сдвигового регистра.
● В 10-битном режиме адресации отправка сопровождается следующими событиями:
Устанавливается бит ADD10 аппаратно и генерируется прерывание, если оно разрешено в ITEVTEN.
Затем мастер ожидает чтения регистра SR1, после чего в DR записывается вторая часть адреса.
Устанавливается бит ADDRаппаратно и генерируется прерывание, если оно разрешено в ITEVTEN.
Затем мастер ожидает чтения регистра SR1 и регистра SR3, что позволяет сбросить бит ADDR и продолжить передачу.
● В 7-битном режиме адресации отправляется только один байт.
Сразу же после отправки байта адреса устанавливается аппаратно бит ADDR и генерируется прерывание, если оно разрешено в ITEVTEN.
Затем мастер ожидает чтения регистра SR1 и регистра SR3.
Мастер может решать перейти в режим передачи или приема в соответствии с 0-м битом слейв-адреса.
Если нулевой бит = 0, то передача, если = 1, то прием.
● В режиме 10-битной адресации:
– Для входа в режим передачи мастер отправляет заголовок (11110xx0) и слейв-адрес (где xx– это два старших бита адреса).
– Для входа в режим приема мастер отправляет заголовок (11110xx0) слейв-адрес. Затем оправляется повторная реСТАРТ-посылка,затем заголовок (11110xx1).
Бит TRA показывает в каком режиме находится мастер: прием или передача.
Мастер в режиме передатчика
После передачи адреса и очистки бита ADDR, мастер отправляет байт из регистра DR на SDA линию через внутренний сдвиговый регистр.
Мастер ожидает до тех пор, пока в DR запишется первый байт данных (событие EV8_1).
Когда принят сигнал (импульс) подтверждения:
● Устанавливается аппаратно бит TXE и генерируется прерывание, если оно разрешено в ITEVTEN, а также устанавливается бит ITBUFEN.
● Если TXE установлен и байт данных не был записан в DR до окончания следующей передачи данных, устанавливается бит BTF, и интерфейс ожидает, пока он не будет очищен, что делается чтением регистра SR1 и записью в DR, SCL удерживается в низком состоянии.
Завершения связи (передачи)
После записи последнего байта в DR, устанавливают бит STOP, который вызывает генерацию СТОП посылки (событие EV8_2). Интерфейс автоматически переходит в режим слейв. (MSLбит сбрасывается).
Заметка: Стоп-посылка должна быть запрограммирована во время события EV8_2, когда TXE или BTF установлены.

S= Старт,
Sr= Повторный старт,
P= Стоп,
A= Подтвеждение,
NA= неподтверждение,
EVx= событие
EV5:SB=1 очищается чтением регистра SR1, далее записывается адрес в DR.
EV6:ADDR=1, очищается чтением регистра SR1 и последующим чтением SR3.
EV8_1:TXE=1, сдвиговый регистр пуст, регистр данных пуст, запись данных в DR.
EV8:TXE=1, сдвиговый регистр не пуст, регистр данных DR пуст, очищается записью в DR.
EV8_2:TXE=1, BTF= 1, Программируется STOP запрос. TXEи BTF очищаются аппаратно после генерации стоп-посылки.
EV9:ADD10=1, очищается чтением регистра SR1, далее записывается регистр DR.
Событие EV8 должно быть выполнено до конца передачи текущего байта. В противном случае рекомендуется использовать BTF вместо TXE с замедлением связи.
Мастер в режиме приемника
После передачи адреса и очистки бита ADDR I2C интерфейс входит в режим приемника. В этом режиме интерфейс принимает байты из SDA линии в DR регистр через внутренний сдвиговый регистр.
После каждого байта интерфейс генерирует последовательность:
● Подтверждающий импульс, если бит ACK установлен
● Установка бита RXNE и генерация прерывания, если биты ITEVTEN и ITBUFEN установлены.
Если бит RXNE установлен и данные не были прочитаны из DR до того, как был принят следующий байт, аппаратно устанавливается бит BTF и интерфейс ожидает, пока этот бит будет очищен чтением I2C_SR1 и I2C_DR, SCL удерживается в низком состоянии.
Завершение соединения
Метод 1: Этот метод подходит в том случае, если I2C использует прерывания, которые имеют наивысший приоритет в приложении.
Мастер отправляет NACK в конце последнего байта от слейва.
После приема этого NACK слейв освобождает линии SCL и SDA. Затем мастер может отправить Стоп или Рестарт посылку.
● В случает генерации NACK импульса после последнего принятого байта, бит ACK должен быть очищен точно после чтения предпоследнего байта (после предпоследнего события RXNE).
● В случае генерации Стоп или Рестарта приложение должно установить бит STOP/START сразу после чтения предпоследнего байта (после предпоследнего события RXNE).
● В случае приема одного байта, подтверждение деактивируется и генерируется СТОП после события EV6 (в EV6 сразу после очистки ADDR).
После генерации СТОП посылки интерфейс автоматически переходит в режим слейва. (MSL= 0).

EV5:SB=1, очищается чтением SR1 с последующей записью в DR.
EV6:ADDR=1, очищается чтением SR1 и последующим чтением SR3. В 10-битном режиме мастера-приемника эта последовательность должна следовать после записи в CR2 бита START= 1.
EV6_1:нет флагов данного события, используется только для однобайтного приема. Программируется ACK=0 и STOP=1 после очистки ADDR.
EV7:RXNE=1, очищается чтением DR.
EV7_1:RXNE=1, очищается чтением DR, программируется ACK=0 и STOP запрос
EV9:ADD10=1, очищается чтением SR1 с последующей записью в DR.
1.Если регистр DR заполнен, прием следующих данных выполняется после очистки события EV7.
2. EV5, EV6 и EV9 события удерживают SCL в низкоуровневом состоянии до конца соответствующей последовательности действий в приложении.
3. EV7 программная последовательность должна быть завершена до конца передачи текущего байта. В противном случае рекомендуется использовать BTF вместо RXNE, тем самым замедляя скорость связи.
4. Последовательность EV6_1 или EV7_1 должна быть завершена до импульса подтверждения ACK текущего байта.
Метод 2: Этот метод подходит для тех случаев, когда используются прерывания, которые не имеют наивысшего приоритета в приложении и когда I2C используется в режиме опроса.
● DataN_2 не читается, так что после DataN_1 соединение замедляется (оба бита установлены RxNE и BTF).
● Далее, бит ACK должен быть очищен до чтения DataN-2 из DR для уверенности, что этот бит был очищен до импульса подтверждения DataN.
● После этого сразу после чтения DataN_2, приложения должно установить бит STOP/START и прочитать DataN_1. После установки RXNE читается DataN.

EV5: SB=1, очищается чтением SR1 с последующей записью в DR.
EV6:ADDR1, очищается чтением SR1 с последующим чтением SR3.
В 10-битном режиме мастера-приемника эта последовательность должна следовать после записи в CR2 бита START= 1.
EV7: RxNE=1, очищается чтением DR.
EV7_2: BTF= 1, DataN-2 в DR, а DataN-1 в сдвиговом регистре, программируется ACK= 0, читаются данные DataN-2 из DR. Устанавливается STOP= 1, читается DataN-1.
EV9: ADD10= 1, очищается чтением SR1 с последующей записью в DR.
Когда остается прочитать 3 байта (N>2):
● RxNE= 1 => ничего не делать (DataN-2 не читаем).
● DataN-1 принят
● BTF= 1 потому что сдвиговый и регистр данных заполнены: DataN-2 в DR и DataN-1 в сдвиговом регистре => SCL удерживается в низком состоянии: никакие другие данные не будут получены из шины.
● Сбрасываем ACK= 0
● Читаем DataN-2 из DR=> Это позволяет DataN попасть в сдвиговый регистр
● DataN принят (с посылкой NACK)
● Программируется бит START/STOP
● Читаются данные DataN-1
● Ожидается установка RxNE= 1
● Читаются данные DataN
Процедура, описанная выше, подходит для случая, когда нужно принять байт N>2.
Когда нужно принять один (N=1) байт или два(N=2), обработка будет отличаться:
● Случай N=1
– Ожидаем установки ADDR, сбрасывает бит ACK=0.
– Сбрасывает ADDR=0
– Устанавлием бит STOP/START.
– Читаем данные, после того, как установится RxNE.
● Случай N=2
– Устанавливаются POS=1 и ACK=1
– Ожидается установка ADDR=1
– Очищается ADDR
–Очищается ACK
– Ожидается установка BTF=1
–Программируется STOP=1
– Читаем DR дважды

EV5: SB=1, очищается чтением SR1 с последующей записью в DR.
EV6:ADDR1, очищается чтением SR1 с последующим чтением SR3.
В 10-битном режиме мастера-приемника эта последовательность должна следовать после записи в CR2 бита START= 1.
EV6_1:нет флагов данного события. Подтверждение должно быть отключено сразу после события EV6, после чего ADDRочищается
EV7_3: BTF= 1, программируется STOP=1, дважды читается регистр DR сразу после установки бита STOP.
EV9: ADD10= 1, очищается чтением SR1 с последующей записью в DR.
EV5, EV6 и EV9 события удерживают SCLв низкоуровневом состоянии до конца соответствующей последовательности действий в приложении.
EV6_1 последовательность должна быть завершена до установки импульса подтверждения ACK для текущего передаваемого байта.
Ну и наконец пример кода:
//Результат выполнения операции с i2c
typedef enum {
I2C_SUCCESS = 0,
I2C_TIMEOUT,
I2C_ERROR
} t_i2c_status;
//Таймаут ожидания события I2C
static unsigned long int i2c_timeout;
//Задать таймаут в микросекундах
#define set_tmo_us(time)
i2c_timeout = (unsigned long int)(F_MASTER_MHZ * time)
//Задать таймаут в миллисекундах
#define set_tmo_ms(time)
i2c_timeout = (unsigned long int)(F_MASTER_MHZ * time * 1000)
#define tmo i2c_timeout--
//Ожидание наступления события event
//в течении времени timeout в мс
#define wait_event(event, timeout) set_tmo_ms(timeout);
while(event && i2c_timeout);
if(!i2c_timeout) return TIMEOUT;
//******************************************************************************
// Инициализация I2C интерфейса
// f_master_hz - частота тактирования периферии Fmaster
// f_i2c_hz - скорость передачи данных по I2C
//******************************************************************************
void i2c_master_init(unsigned long f_master_hz, unsigned long f_i2c_hz){
unsigned long int ccr;
PB_DDR_bit.DDR4 = 0;
PB_DDR_bit.DDR5 = 0;
PB_ODR_bit.ODR5 = 1; //SDA
PB_ODR_bit.ODR4 = 1; //SCL
PB_CR1_bit.C14 = 0;
PB_CR1_bit.C15 = 0;
PB_CR2_bit.C24 = 0;
PB_CR2_bit.C25 = 0;
//Частота тактирования периферии MHz
I2C_FREQR_FREQ = 12;
//Отключаем I2C
I2C_CR1_PE = 0;
//В стандартном режиме скорость I2C max = 100 кбит/с
//Выбираем стандартный режим
I2C_CCRH_F_S = 0;
//CCR = Fmaster/2*Fiic= 12MHz/2*100kHz
ccr = f_master_hz/(2*f_i2c_hz);
//Set Maximum Rise Time: 1000ns max in Standard Mode
//= [1000ns/(1/InputClockFrequencyMHz.10e6)]+1
//= InputClockFrequencyMHz+1
I2C_TRISER_TRISE = 12+1;
I2C_CCRL = ccr & 0xFF;
I2C_CCRH_CCR = (ccr >> 8) & 0x0F;
//Включаем I2C
I2C_CR1_PE = 1;
//Разрешаем подтверждение в конце посылки
I2C_CR2_ACK = 1;
}
//******************************************************************************
// Запись регистра slave-устройства
//******************************************************************************
t_i2c_status i2c_wr_reg(unsigned char address, unsigned char reg_addr,
char * data, unsigned char length)
{
//Ждем освобождения шины I2C
wait_event(I2C_SR3_BUSY, 10);
//Генерация СТАРТ-посылки
I2C_CR2_START = 1;
//Ждем установки бита SB
wait_event(!I2C_SR1_SB, 1);
//Записываем в регистр данных адрес ведомого устройства
I2C_DR = address & 0xFE;
//Ждем подтверждения передачи адреса
wait_event(!I2C_SR1_ADDR, 1);
//Очистка бита ADDR чтением регистра SR3
I2C_SR3;
//Ждем освобождения регистра данных
wait_event(!I2C_SR1_TXE, 1);
//Отправляем адрес регистра
I2C_DR = reg_addr;
//Отправка данных
while(length--){
//Ждем освобождения регистра данных
wait_event(!I2C_SR1_TXE, 1);
//Отправляем адрес регистра
I2C_DR = *data++;
}
//Ловим момент, когда DR освободился и данные попали в сдвиговый регистр
wait_event(!(I2C_SR1_TXE && I2C_SR1_BTF), 1);
//Посылаем СТОП-посылку
I2C_CR2_STOP = 1;
//Ждем выполнения условия СТОП
wait_event(I2C_CR2_STOP, 1);
return I2C_SUCCESS;
}
//******************************************************************************
// Чтение регистра slave-устройства
// Start -> Slave Addr -> Reg. addr -> Restart -> Slave Addr <- data ... -> Stop
//******************************************************************************
t_i2c_status i2c_rd_reg(unsigned char address, unsigned char reg_addr,
char * data, unsigned char length)
{
//Ждем освобождения шины I2C
wait_event(I2C_SR3_BUSY, 10);
//Разрешаем подтверждение в конце посылки
I2C_CR2_ACK = 1;
//Генерация СТАРТ-посылки
I2C_CR2_START = 1;
//Ждем установки бита SB
wait_event(!I2C_SR1_SB, 1);
//Записываем в регистр данных адрес ведомого устройства
I2C_DR = address & 0xFE;
//Ждем подтверждения передачи адреса
wait_event(!I2C_SR1_ADDR, 1);
//Очистка бита ADDR чтением регистра SR3
I2C_SR3;
//Ждем освобождения регистра данных RD
wait_event(!I2C_SR1_TXE, 1);
//Передаем адрес регистра slave-устройства, который хотим прочитать
I2C_DR = reg_addr;
//Ловим момент, когда DR освободился и данные попали в сдвиговый регистр
wait_event(!(I2C_SR1_TXE && I2C_SR1_BTF), 1);
//Генерация СТАРТ-посылки (рестарт)
I2C_CR2_START = 1;
//Ждем установки бита SB
wait_event(!I2C_SR1_SB, 1);
//Записываем в регистр данных адрес ведомого устройства и переходим
//в режим чтения (установкой младшего бита в 1)
I2C_DR = address | 0x01;
//Дальше алгоритм зависит от количества принимаемых байт
//N=1
if(length == 1){
//Запрещаем подтверждение в конце посылки
I2C_CR2_ACK = 0;
//Ждем подтверждения передачи адреса
wait_event(!I2C_SR1_ADDR, 1);
//Заплатка из Errata
__disable_interrupt();
//Очистка бита ADDR чтением регистра SR3
I2C_SR3;
//Устанавлием бит STOP
I2C_CR2_STOP = 1;
//Заплатка из Errata
__enable_interrupt();
//Ждем прихода данных в RD
wait_event(!I2C_SR1_RXNE, 1);
//Читаем принятый байт
*data = I2C_DR;
}
//N=2
else if(length == 2){
//Бит который разрешает NACK на следующем принятом байте
I2C_CR2_POS = 1;
//Ждем подтверждения передачи адреса
wait_event(!I2C_SR1_ADDR, 1);
//Заплатка из Errata
__disable_interrupt();
//Очистка бита ADDR чтением регистра SR3
I2C_SR3;
//Запрещаем подтверждение в конце посылки
I2C_CR2_ACK = 0;
//Заплатка из Errata
__enable_interrupt();
//Ждем момента, когда первый байт окажется в DR,
//а второй в сдвиговом регистре
wait_event(!I2C_SR1_BTF, 1);
//Заплатка из Errata
__disable_interrupt();
//Устанавлием бит STOP
I2C_CR2_STOP = 1;
//Читаем принятые байты
*data++ = I2C_DR;
//Заплатка из Errata
__enable_interrupt();
*data = I2C_DR;
}
//N>2
else if(length > 2){
//Ждем подтверждения передачи адреса
wait_event(!I2C_SR1_ADDR, 1);
//Заплатка из Errata
__disable_interrupt();
//Очистка бита ADDR чтением регистра SR3
I2C_SR3;
//Заплатка из Errata
__enable_interrupt();
while(length-- > 3 && tmo){
//Ожидаем появления данных в DR и сдвиговом регистре
wait_event(!I2C_SR1_BTF, 1);
//Читаем принятый байт из DR
*data++ = I2C_DR;
}
//Время таймаута вышло
if(!tmo) return I2C_TIMEOUT;
//Осталось принять 3 последних байта
//Ждем, когда в DR окажется N-2 байт, а в сдвиговом регистре
//окажется N-1 байт
wait_event(!I2C_SR1_BTF, 1);
//Запрещаем подтверждение в конце посылки
I2C_CR2_ACK = 0;
//Заплатка из Errata
__disable_interrupt();
//Читаем N-2 байт из RD, тем самым позволяя принять в сдвиговый
//регистр байт N, но теперь в конце приема отправится посылка NACK
*data++ = I2C_DR;
//Посылка STOP
I2C_CR2_STOP = 1;
//Читаем N-1 байт
*data++ = I2C_DR;
//Заплатка из Errata
__enable_interrupt();
//Ждем, когда N-й байт попадет в DR из сдвигового регистра
wait_event(!I2C_SR1_RXNE, 1);
//Читаем N байт
*data++ = I2C_DR;
}
//Ждем отправки СТОП посылки
wait_event(I2C_CR2_STOP, 1);
//Сбрасывает бит POS, если вдруг он был установлен
I2C_CR2_POS = 0;
return I2C_SUCCESS;
}
