STM32 I2C.

STM32 I2C.

В последнее время все чаще натыкаюсь на негативные отзывы о шине I2C у STM32, мол работа с ней это танцы с бубном и тд.
За последний месяц мне удалось запустить две микросхемы, работающие по I2C и ни каких танцев, только вдумчивое чтение даташита.

Модуль I2C у STM32 обладает следующими особенностями:
  • может работать в двух режимах Fm(fast mode) и Sm(standart mode), первый работает на частотах до 400KHz, второй до 100KHz
  • буфер размером 1 байт с поддержкой DMA
  • поддерживает аппаратный подсчет контрольной суммы
  • на его основе возможна реализуется SMBus(System Management Bus) и PMBus(Power Management Bus)
  • два вектора прерывания, генерируются при успешной передаче и при возникновении ошибки
  • фильтр для борьбы с шумами
  • может работать в режиме Master или Slave

В режиме Master:
  • генерирует тактирующий сигнал
  • генерирует START и STOP

В режиме Slave:
  • можно программировать основной и альтернативный адрес на который он будет отзываться
  • определяет STOP


По умолчанию модуль находится в режиме Slave, но он автоматически переключается в режим Master после генерации состояния START.
Принципиальное отличие между Master и Slave, в том, что Master генерирует тактовый сигнал и всегда инициирует передачу данных и заканчивает её. Slave же, откликается на свой адрес и широковещательный, при чем отклик на широковещательный адрес можно отключить. Также Slave генерирует состояние ACK, но его тоже можно отключить.

Такое подробное разъяснение необходимо потому, что в обоих режимах устройство может выступать как передатчиком, так и приемником.
  • Slave transmitter
  • Slave receiver
  • Master transmitter
  • Master receiver


Ниже показана структура модуля I2C.
STM32 I2C.


Регистр управления I2C_CR1:

SWRST(Software reset) — единица в этом бите сбрасывает значение всех регистров модуля в дефолтное стояние, может использоваться для сброса при возникновении ошибки.

ALERT(SMBus alert) — установка единицы в этот бит разрешает генерировать сигнал alert в режиме SMBus.

PEC(Packet error checking) — управление этим битом производится программно, но он может быть сброшен аппаратно когда передается PEC, START, STOP или PE=0. Единица в этом бите разрешает передачу CRC.

POS(Acknowledge/PEC Position (for data reception)) — состояние этого бита определяет положение ACK/PEC в двух байтовой конфигурации в режиме Master.

ACK(Acknowledge enable) — единица в этом бите разрешает отправлять ACK/NACK после приема байта адреса или данных.

STOP(Stop generation) — установка единицы в этот бит генерирует сигнал STOP в режиме Master.

START(Start generation) — установка единицы в этот бит генерирует состояние START в режиме Master,

NOSTRETCH(Clock stretching disable (Slave mode)) — если на обработку данных требуется время Slave может остановить передачу мастера, прижав линию SCL к земле, Master будет ждать и не будет ни чего слать, пока линия не будет отпущена. Ноль в этом бите прижимает SCL к земле.

ENGC(General call enable) — если в этом бите установлена единица, модуль отвечает ACKом на широковещательный адрес 0х00.

ENPEC(PEC enable) — установка единицы в этот бит включает аппаратный подсчет CRC.

ENARP(ARP enable) — установка единицы в этот бит включает ARP.

SMBTYPE(SMBus type) — если в этом бите установлен ноль модуль работает в режиме Slave, если единица в режиме Master.

SMBUS(SMBus mode) — если в этом бите установлен ноль модуль работает в режиме I2C, если единица SMBus.

PE(Peripheral enable) — единица в этом бите включает модуль.


Регистр управления I2C_CR2:

LAST(DMA last transfer) — единица в этом бите разрешает DMA генерировать сигнал окончания передачи EOT(End of Transfer).

DMAEN(DMA requests enable) — единица в этом бите разрешает делать запрос к DMA при установке флагов TxE или RxNE.

ITBUFEN(Buffer interrupt enable) — если этот бит сброшен, разрешены все прерывания, кроме прерываний по приему и передаче.

ITEVTEN(Event interrupt enable) — единица в этом бите разрешает прерывания по событию.

ITERREN(Error interrupt enable) — единица в этом бите разрешает прерывания при возникновении ошибок.

FREQ[5:0](Peripheral clock frequency) — в это битовое битовое поле необходимо записать частоту тактирования модуля, она может принимать значение от 2 до 50.


Регистр I2C_OAR1:

ADDMODE(Addressing mode) — этот бит определяет размер адреса Slave, ноль соответствует размеру адреса 7 бит, единица — 10 бит.

ADD[9:8](Interface address) — старшие биты адреса, в случае если адрес 10-битный.

ADD[1:7](Interface address) — адрес устройства.

ADD0(Interface address) — младший бит адреса, в случае если адрес 10-битный..


Регистр I2C_OAR2:

ADD2[7:1] — альтернативный адрес на который будет отзываться Slave.

ENDUAL(Dual addressing mode enable) — единица в этом бите разрешает Slave отзываться на альтернативный адрес в 7-битном режиме.


I2C_DR — регистр данных, для отправки данных пишем в регистр DR, для приёма читаем его же.


Регистр статуса I2C_SR1:

SMBALERT(SMBus alert) — возникает в случае alert в шине SMBus.

TIMEOUT (Timeout or Tlow error) — возникает если линия SCL прижата к земле. Для master 10mS, для slave 25mS.

PECERR (PEC Error in reception) — возникает при ошибке PEC при приеме.

OVR (Overrun/Underrun) — возникает при переполнении данных.

AF (Acknowledge failure) — устанавливается при получении сигнала NACK. Для сброса нужно записать 0.

ARLO (Arbitration lost (master mode) ) — устанавливается при потере арбитража. Для сброса нужно записать 0.

BERR (Bus error) — ошибка шины. Устанавливается в случае возникновения сигнала START или STOP в неправильный момент.

TxE (Data register empty (transmitters)) — устанавливается при опустошении регистра DR, а точнее когда данные из него были перемещены в сдвиговый регистр.

RxNE (Data register not empty (receivers)) — устанавливается при приеме байта данных, кроме адреса.

STOPF(Stop detection (slave mode)) — при работе в режиме slave устанавливается при обнаружении сигнала STOP, если перед этим был сигнал ACK. Для сброса необходимо прочитать SR1 и произвести запись в CR1.

ADD10 (10-bit header sent (Master mode)) — устанавливается при отправке первого байта 10-битного адреса.

BTF (Byte transfer finished) — флаг устанавливается по окончании приема/передачи байта, работает только при NOSTRETCH равном нулю.

ADDR(Address sent (master mode)/matched (slave mode)) — в режиме master устанавливается после передачи адреса, в режиме slave устанавливается при совпадении адреса. Для сброса нужно прочитать регистр SR1, а затем SR2.

SB(Start bit (Master mode)) — устанавливается при возникновении сигнала START. Для сброса флага необходимо прочитать SR1 и записать данные в регистр DR .


Регистр статуса I2C_SR2:

PEC[7:0](Packet error checking register) — в это битовое поле записывается контрольная сумма кадра.

DUALF(Dual flag (Slave mode)) — ноль в этом бите говорит о том, что адрес который принял Slave соответствует OAR1, иначе OAR2.

SMBHOST(SMBus host header (Slave mode)) — устанавливается, когда принят заголовок SMBus Host.

SMBDEFAULT(SMBus device default address (Slave mode)) — устанавливается, если принят адрес по умолчанию
для SMBus-устройства.

GENCALL(General call address (Slave mode)) — устанавливается, если принят широковещательный адрес в режиме ведомого.

TRA(Transmitter/receiver) — единица в этом бите говорит о том, что модуль работает как передатчик, иначе приемник.

BUSY(Bus busy) — флаг занятости.

MSL(Master/slave) — единица в этом бите говорит о том, что модуль работает в режиме Master, иначе Slave.


Регистр управления частотой I2C_CCR:

F/S(I2C master mode selection) — при установке единицы в этот бит модуль работает в режиме FAST, иначе STANDART.

DUTY (Fm mode duty cycle) — этот бит задает скважность сигнала SCL в режиме FAST. Если установлен ноль tlow/thigh = 2, иначе tlow/thigh = 16/9.

CCR[11:0](Clock control register in Fm/Sm mode (Master mode)) — при работе в режиме Master задает тактовую частоту линии SCL.

Sm mode or SMBus:
Thigh = CCR * TPCLK1
Tlow = CCR * TPCLK1

Fm mode:
If DUTY = 0:
Thigh = CCR * TPCLK1
Tlow = 2 * CCR * TPCLK1

If DUTY = 1: (to reach 400 kHz)
Thigh = 9 * CCR * TPCLK1
Tlow = 16 * CCR * TPCLK1

Далее надо подставить значения Thigh и Tlow в след формулу
Tscl = Thigh + Tlow
где Tscl для SM 10uS, а для FM 2,5 uS.

Получаем для режима SM следующее:
CCR * TPCLK1 + CCR * TPCLK1 = 10 000ns
CCR = 10 000/(2* TPCLK1)

Регистр I2C_TRISE:

TRISE[5:0] — определяет время нарастания фронта. Рассчитывается по формуле (Tr max/TPCLK1)+1,
где Tr max для SM составляет 1000nS, а для FM 300nS,
а TPCLK1 — период который рассчитывается как 1/F(APB1).


Регистр управления фильтрами I2C_FLTR:

ANOFF(Analog noise filter OFF) — ноль в этом бите включает аналоговый фильтр.

DNF[3:0](Digital noise filter) — битовое поле для настройки цифрового фильтра. За подробностями нужно обратиться к документации.

Инициализация модуля из рабочего проекта.

void I2C2_Init(void)
{
	/*
			SDL -> PB10
			SDA -> PB11
			RST -> PE15
	*/
	
        //включаем тактирование портов и модуля I2C
	RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN | RCC_AHB1ENR_GPIOEEN;
	RCC->APB1ENR |=	RCC_APB1ENR_I2C2EN;
	
	//альтернативная ф-ция, выход с открытым стоком, 2 MHz
	GPIOB->AFR[1] |= (0x04<<2*4);
	GPIOB->AFR[1] |= (0x04<<3*4);
	
	GPIOB->MODER |= GPIO_MODER_MODER10_1;
	GPIOB->OTYPER |= GPIO_OTYPER_OT_10;
	GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR10;
	
	GPIOB->MODER |= GPIO_MODER_MODER11_1;
	GPIOB->OTYPER |= GPIO_OTYPER_OT_11;
	GPIOB->OSPEEDR &= ~GPIO_OSPEEDER_OSPEEDR11;
	
	//PE15 двухтактный выход 50MHz 
	GPIOE->MODER |= GPIO_MODER_MODER15_0;
        GPIOE->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR15;
	AU_RST_HIGH
	
	//настраиваем модуль в режим I2C
	I2C2->CR1 &= ~2C_CR1_SMBUS;
	
        //указываем частоту тактирования модуля
	I2C2->CR2 &= ~I2C_CR2_FREQ;
	I2C2->CR2 |= 42; // Fclk1=168/4=42MHz 
	
        //конфигурируем I2C, standart mode, 100 KHz duty cycle 1/2	
	I2C2->CCR &= ~(I2C_CCR_FS | I2C_CCR_DUTY);
        //задаем частоту работы модуля SCL по формуле 10 000nS/(2* TPCLK1) 
	I2C2->CCR |= 208; //10 000ns/48ns = 208
	
	//Standart_Mode = 1000nS, Fast_Mode = 300nS, 1/42MHz = 24nS
	I2C2->TRISE = 42; //(1000nS/24nS)+1

        //включаем модуль
	I2C2->CR1 |= I2C_CR1_PE;
}

void I2C_Write(uint8_t reg_addr, uint8_t data)
{
        //стартуем
        I2C2->CR1 |= I2C_CR1_START;		
	while(!(I2C2->SR1 & I2C_SR1_SB)){};
	(void) I2C2->SR1;
		
        //передаем адрес устройства
	I2C2->DR = I2C_ADDRESS(ADDR,I2C_MODE_WRITE);
	while(!(I2C2->SR1 & I2C_SR1_ADDR)){};
	(void) I2C2->SR1;
	(void) I2C2->SR2;
		
        //передаем адрес регистра
	I2C2->DR = reg_addr;	
	while(!(I2C2->SR1 & I2C_SR1_TXE)){};	
			
        //пишем данные	
	I2C2->DR = data;	
	while(!(I2C2->SR1 & I2C_SR1_BTF)){};	
	I2C2->CR1 |= I2C_CR1_STOP;		
}

uint8_t I2C_Read(uint8_t reg_addr)
{
	uint8_t data;
	//стартуем
	I2C2->CR1 |= I2C_CR1_START;		
	while(!(I2C2->SR1 & I2C_SR1_SB)){};
	(void) I2C2->SR1;

	//передаем адрес устройства	
	I2C2->DR = I2C_ADDRESS(ADR,I2C_MODE_WRITE);
	while(!(I2C2->SR1 & I2C_SR1_ADDR)){};
	(void) I2C2->SR1;
	(void) I2C2->SR2;

	//передаем адрес регистра
	I2C2->DR = reg_addr;	
	while(!(I2C2->SR1 & I2C_SR1_TXE)){};	
	I2C2->CR1 |= I2C_CR1_STOP;	
			
	//рестарт!!!
	I2C2->CR1 |= I2C_CR1_START;		
	while(!(I2C2->SR1 & I2C_SR1_SB)){};
	(void) I2C2->SR1;
		
	//передаем адрес устройства, но теперь для чтения
	I2C2->DR = I2C_ADDRESS(ADR,I2C_MODE_READ);
	while(!(I2C2->SR1 & I2C_SR1_ADDR)){};
	(void) I2C2->SR1;
	(void) I2C2->SR2;
			
	//читаем	
	I2C2->CR1 &= ~I2C_CR1_ACK;
	while(!(I2C2->SR1 & I2C_SR1_RXNE)){};
	data = I2C2->DR;	
	I2C2->CR1 |= I2C_CR1_STOP;	
			
	return data;	
} 	
комментарии
2