Микроконтроллеры

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » Микроконтроллеры » Архив » STM32 I2C


STM32 I2C

Сообщений 1 страница 30 из 30

1

I2C    На примере F4 дискавери.

Определяемся с документацией и терминами.

Выкачиваем    UM10204  I2C-bus specification and user manual 
   стр. 48-50

http://s1.uploads.ru/yABKN.png



                                                                                Спецификация таймингов шины для SM и FM.
http://s4.uploads.ru/t/j1CE3.png                           http://s8.uploads.ru/t/E9FR3.png

   
Спецификация таймингов для SCL
Tr          -Time rise  время нарастания импульса
Tf             - время спада импульса
TLOW    -период паузы
THIGH   -период импульса
FSCL      -  частота SCL  для SM - 100КГц для FM - 400КГц
TSCL      -  период SCL  TSCL =1/FSCL = Tf +TLOW  +Tr +THIGH
                              для SM 10uS   для FM 2,5 uS
   
     Таблица 10    UM10204

http://s9.uploads.ru/jl6oP.png

   Tr max  - для SM - 1000 nS для FM - 300nS
   Tf max  -для SM - 300nS для FM – 300nS

Конфигурируем модуль i2c:
Включаем тактирование GPIOB и модуля I2C1

Код:
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;    
	RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;

Переключаем AF для пинов PB7 (SDA)и PB8 (SCL) на AF4 см. Table 9. Alternate function mapping (continued) даташита.   

http://s7.uploads.ru/fFCnR.png

Код:
GPIOB->AFR[0] |= (4<<(4*7));
	GPIOB->AFR[1] |= (4<<(4*0));

Переводим пины в режим AF OD ( Open Drain). Пуллапы внешние !!!!

Код:
GPIOB->MODER &= ~(GPIO_MODER_MODER8 |GPIO_MODER_MODER7);
	GPIOB->MODER |= (GPIO_MODER_MODER8_1 |  GPIO_MODER_MODER7_1);
	GPIOB->OTYPER |=(GPIO_OTYPER_OT_8 |GPIO_OTYPER_OT_7);

RM0090
   стр. 865

27.6.2 I2C Control register 2 (I2C_CR2)

Bits 5:0 FREQ[5:0]: Peripheral clock frequency
The FREQ bits must be configured with the APB clock frequency value (I2C peripheral
connected to APB). The FREQ field is used by the peripheral to generate data setup and
hold times compliant with the I2C specifications. The minimum allowed frequency is 2 MHz,
the maximum frequency is limited by the maximum APB frequency and cannot exceed
50 MHz (peripheral intrinsic maximum limit).
0b000000: Not allowed
0b000001: Not allowed
0b000010: 2 MHz
...
0b110010: 50 MHz
Higher than 0b100100: Not allowed

 
           Вкратце:
    В  FREQ[5:0]  частота APB (от которой тактируется I2C, в нашем случае APB1) в МГц .
    В  нашей конфигурации частота APB1 -42МГц .
    И далее в RM  под частотой тактирования I2C -FPCLK1 подразумевается  частота APB1.
    TPCLK1 соотвественно период и равен  1/FPCLK1 и в нашем случае 1/42 000 000  = 24nS

Заносим в FREQ значение FPCLK1(APB1)
 

Код:
     I2C1->CR2 &= ~I2C_CR2_FREQ;
     I2C1->CR2 |= 42;

27.6.8 I2C Clock control register (I2C_CCR)

   Bit 15 F/S: I2C master mode selection
         0: Sm mode I2C
         1: Fm mode I2C

     F/S  -  выбор режима SM Standart mode – 100КГц  или FM Fast mode - 400КГц

Bit 14 DUTY: Fm mode duty cycle
         0: Fm mode tlow/thigh = 2
         1: Fm mode tlow/thigh = 16/9 (see CCR)

    Выбор соотношения   TLOW /THIGH   в FM  - 1:2 или 16:9

Bits 11:0 CCR[11:0]: Clock control register in Fm/Sm mode (Master mode)
Controls the SCL clock in master mode.

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

For instance: in Sm mode, to generate a 100 kHz SCL frequency:
If FREQR = 08, TPCLK1 = 125 ns so CCR must be programmed with 0x28
(0x28 <=> 40d x 125 ns = 5000 ns.)
Note: The minimum allowed value is 0x04, except in FAST DUTY mode where the minimum
allowed value is 0x01
thigh = tr(SCL) + tw(SCLH). See device datasheet for the definitions of parameters.
tlow = tf(SCL) + tw(SCLL). See device datasheet for the definitions of parameters.
I2C communication speed, fSCL ~ 1/(thigh + tlow). The real frequency may differ due to
the analog noise filter input delay.
The CCR register must be configured only when the I2C is disabled (PE = 0).

  Смотрим вышеописанные тайминги SCL -
TSCL =1/FSCL = Tf +TLOW  +Tr +THIGH
У STM значения Tf Tr не учитываются
TLOW  +THIGH = TSCL

  Для SM или SMBUS :
TSCL - 10 uS
TPCLK1  - 24nS   
TLOW  +THIGH = 10uS

 
CCR * TPCLK1 + CCR * TPCLK1=10 000nS
CCR= 10 000nS/ 2 *  TPCLK1
CCR= 208

   Для FM 

TSCL - 2500nS
TPCLK1  - 24nS   
TLOW  +THIGH = 2500nS

Если DUTY=0
     
2*CCR * TPCLK1 + CCR * TPCLK1= 2500nS
CCR= 2500nS/ 3*  TPCLK1
CCR= 34   

 

  Если DUTY=1

16*CCR * TPCLK1 + 9*CCR * TPCLK1=2500nS
CCR= 2500nS/ 25*  TPCLK1
CCR= 4       
                           

    У нас Fast Mode и DUTY=0
Заносим значение CCR и включаем Fast mode
           

Код:
 I2C1->CCR &= ~I2C_CCR_CCR;
	I2C1->CCR |= 34;
	I2C1->CCR |= I2C_CCR_FS;

27.6.9 I2C TRISE register (I2C_TRISE)

Bits 5:0 TRISE[5:0]: Maximum rise time in Fm/Sm mode (Master mode)
These bits should provide the maximum duration of the SCL feedback loop in master mode.
The purpose is to keep a stable SCL frequency whatever the SCL rising edge duration.
These bits must be programmed with the maximum SCL rise time given in the I2C bus
specification, incremented by 1.
For instance: in Sm mode, the maximum allowed SCL rise time is 1000 ns.
If, in the I2C_CR2 register, the value of FREQ[5:0] bits is equal to 0x08 and TPCLK1 = 125 ns
therefore the TRISE[5:0] bits must be programmed with 09h.
(1000 ns / 125 ns = 8 + 1)
The filter value can also be added to TRISE[5:0].
If the result is not an integer, TRISE[5:0] must be programmed with the integer part, in order
to respect the tHIGH parameter.
Note: TRISE[5:0] must be configured only when the I2C is disabled (PE = 0).

TRISE  - максимальное время нарастания импульса,
    в выше описанных таймингах это  Tr max
  Напомню для SM – 1000nS для FM -300nS
   В  TRISE  заносится соотношение (Tr max/TPCLK1)+1
   У нас 
      Tr max   -300nS
       TPCLK1  -24nS
    TRISE=(300nS/24nS)+1= 14

    Это максимальное значение заносим немного меньшее

Код:
I2C1->TRISE = 12;

Включаем модуль
         

Код:
I2C1->CR1 |= I2C_CR1_PE;

http://s8.uploads.ru/t/dl3SU.png

2

Работа с i2c на примере с дисплеем ssd1306

Код:
void i2c_init(void)
	{
    RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;	
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
    
    
    GPIOB->AFR[0] |= (4<<(4*7));
    GPIOB->AFR[1] |= (4<<(4*0));
    GPIOB->MODER &= ~(GPIO_MODER_MODER8 |GPIO_MODER_MODER7);
    GPIOB->MODER |= (GPIO_MODER_MODER8_1 |GPIO_MODER_MODER7_1);
    GPIOB->OTYPER |=(GPIO_OTYPER_OT_8 |GPIO_OTYPER_OT_7);
	
    I2C1->CR2 &= ~I2C_CR2_FREQ;
    I2C1->CR2 |= 42;
    
    I2C1->CCR &= ~I2C_CCR_CCR;
    I2C1->CCR |= 34;
    I2C1->CCR |= I2C_CCR_FS;
	
    I2C1->TRISE = 12;

    I2C1->CR1 |= I2C_CR1_PE;
 
	}

void ssd1306_init(void)
{
	
	i2c_init();
    	
  ssd1306_SendCommand(SSD1306_DISPLAY_OFF);
  ssd1306_SendCommand(SSD1306_SET_DISPLAY_CLOCK_DIV);
  ssd1306_SendCommand(0x80);  
  ssd1306_SendCommand(SSD1306_SET_MULTIPLEX_RATIO);
  ssd1306_SendCommand(0x3F);
  ssd1306_SendCommand(SSD1306_SET_DISPLAY_OFFSET);
  ssd1306_SendCommand(0x00);
  ssd1306_SendCommand(SSD1306_SET_START_LINE | 0x00);
  ssd1306_SendCommand(SSD1306_SET_CHARGE_PUMP);
  ssd1306_SendCommand(0x14);
  ssd1306_SendCommand(SSD1306_MEMORY_ADDRESS_MODE);
  ssd1306_SendCommand(SSD1306_SET_LCOL_START_ADDRESS);
  ssd1306_SendCommand(SSD1306_SEGMENT_REMAP | 0x01);
  ssd1306_SendCommand(SSD1306_COM_SCAN_INVERSE);
  ssd1306_SendCommand(SSD1306_SET_COM_PINS_CONFIG);
  ssd1306_SendCommand(0x12);
  ssd1306_SendCommand(SSD1306_SET_CONTRAST);
  ssd1306_SendCommand(0xf0);
//	ssd1306_SendCommand(0x10);
  ssd1306_SendCommand(SSD1306_SET_PRECHARGE_PERIOD);
  ssd1306_SendCommand(0xF1);
  ssd1306_SendCommand(SSD1306_SET_VCOM_DESELECT_LVL);
  ssd1306_SendCommand(0x40);
  ssd1306_SendCommand(SSD1306_ENTIRE_DISPLAY_RESUME);
  ssd1306_SendCommand(SSD1306_NORMAL_DISPLAY);
  ssd1306_FillDisplay(0x00);
  ssd1306_SendCommand(SSD1306_DISPLAY_ON);
}



	void ssd1306_SendCommand(unsigned char command)
{
  
	ssd1306_send(SSD1306_COMMAND_MODE, command);
	
	
	
}
void 	ssd1306_send(uint8_t control_byte, uint8_t data)
    {


	I2C1->CR1 |= I2C_CR1_START;

    	
    	while (!(I2C1->SR1 & I2C_SR1_SB))
    	{
    	}
    	(void) I2C1->SR1;

    
      
    	I2C1->DR = SSD1306_ADDRESS;
           	
    	while (!(I2C1->SR1 & I2C_SR1_ADDR))
            {
            }
            	(void) I2C1->SR1;
            	(void) I2C1->SR2;
    	
         I2C1->DR=control_byte;
        	while (!(I2C1->SR1 & I2C_SR1_TXE))
            {
            }

	 I2C1->DR=data;
    
    while (!(I2C1->SR1 & I2C_SR1_BTF))
    {
    }
      I2C1->CR1 |= I2C_CR1_STOP;
    
    
        	}
    
void ssd1306_SendData(uint8_t *data, uint8_t count)
{ 
    uint8_t index=0;
	I2C1->CR1 |= I2C_CR1_START;

    	
    	while (!(I2C1->SR1 & I2C_SR1_SB))
    	{
    	}
    	(void) I2C1->SR1;

    
     
    	I2C1->DR = SSD1306_ADDRESS;
           	
    	while (!(I2C1->SR1 & I2C_SR1_ADDR))
            {
            }
            	(void) I2C1->SR1;
            	(void) I2C1->SR2;
    	
         I2C1->DR=SSD1306_DATA_MODE;
        	while (!(I2C1->SR1 & I2C_SR1_TXE))
            {
            }
	
	for(index=0;index<=count-1;index++)
	 {
     I2C1->DR=*data++ ;
     while (!(I2C1->SR1 & I2C_SR1_TXE))
            {
            }
	 }	
	 
	 while (!(I2C1->SR1 & I2C_SR1_BTF))
    {
    }
      I2C1->CR1 |= I2C_CR1_STOP;
	 
}        	
    	
	void ssd1306_SetCursor(unsigned char x, unsigned char p)
{
  ssd1306_SendCommand(SSD1306_SET_LCOL_START_ADDRESS | (x & 0x0F));
  ssd1306_SendCommand(SSD1306_SET_HCOL_START_ADDRESS | (x >> 4));
  ssd1306_SendCommand(SSD1306_SET_PAGE_START_ADDRESS | p);
}        	
        	
        	

	void ssd1306_FillDisplay(unsigned char data)
{
  unsigned char page, x;
 
  
  ssd1306_buf1 = malloc(SSD1306_WIDTH ); // allocate memory for the buf
  
  for (page=0; page<8; page++)
  {	
    ssd1306_SetCursor(0, page);     
    for (x=0; x<SSD1306_WIDTH; x++)
    {
      ssd1306_buf1[x] = data;
    };
    ssd1306_SendData(ssd1306_buf1, SSD1306_WIDTH );
  };
  ssd1306_SetCursor(0, 0);
  free(ssd1306_buf1);  
}

3

Продолжаем учиться работать с документацией.

Работа с i2c eeprom на примере F4 дискавери и 24LC64 от Microchip.
Нам понадобятся RM0090 и даташит на 24AA64/24LC64
Из документации видно что 24LC работает со скоростью до 400КГц (FM).

Адрес самой микросхемы формируется из старших четырех бит и 3х бит chip select, определяемых пинами A2-A0.
В моем случае A2-A0 висят на GND и базовый адрес в итоге - A0.
Функции чтения/записи определяются битом R/#W, таким образом адрес для записи eeprom - A0, для чтения A1;
http://s4.uploads.ru/xQFVR.png

Помимо базового адреса eeprom, нам нужно так же определение адресов внутренних ячеек памяти eeprom.
Последовательность для определения адреса ячейки.
http://s1.uploads.ru/d2wqr.png

Операции чтения/записи eeprom.
Рассмотрим формирование последовательности чтение/записи для произвольного адреса.

На рисунках- красным цветом выделены операции производимые master (контроллер) , зеленым - slave(eeprom).
Запись в произвольный адрес.
http://s2.uploads.ru/KkuRE.png

1. Master формирует Start условие.
2. Master посылает адрес ведомого.
3. Slave подтверждает свой адрес, выставляя ACK(удерживает SDA в 0 )
4. Master передает старший байт адреса ячейки.
5. Slave подтверждает.
6. Master передает младший байт адреса ячейки.
7. Slave подтверждает.
8. Master передает данные для ячейки.
9. Slave подтверждает.
10. Master завершает передачу, формируя Stop условие.

Чтение с произвольного адреса.
http://s6.uploads.ru/ftVOC.png

1. Master формирует Start условие.
2. Master посылает адрес ведомого.
3. Slave подтверждает свой адрес, выставляя ACK(удерживает SDA в 0 )
4. Master передает старший байт адреса ячейки.
5. Slave подтверждает.
6. Master передает младший байт адреса ячейки.
7. Slave подтверждает.
8. Master формирует Restart.
9. Master инициирует чтение, посылая адрес + выставляя бит R/#W в единицу.
10. Slave подтверждает.
11. Slave передает данные ячейки.
12. Master завершает чтение выставляя NAСK и формируя Stop.

Переходим в RM0090 и читаем о формировании ACK, Start и Stop.

Стр. 863 27.6.1 I2C Control register 1 (I2C_CR1)

http://s2.uploads.ru/Aap0M.png

Формируем условие Start.

Код:
I2C1->CR1 |= I2C_CR1_START;

Формируем условие Stop.

Код:
I2C1->CR1 |= I2C_CR1_STOP;

NACK.

Код:
I2C1->CR1 &= ~I2C_CR1_ACK;

Читаем о регистрах статуса и флагах состояний.

27.6.6 I2C Status register 1 (I2C_SR1)

Bit 7 TxE: Data register empty (transmitters)
0: Data register not empty
1: Data register empty
– Set when DR is empty in transmission. TxE is not set during address phase.
– Cleared by software writing to the DR register or by hardware after a start or a stop condition
or when PE=0.
TxE is not set if either a NACK is received, or if next byte to be transmitted is PEC (PEC=1)
Note: TxE is not cleared by writing the first data being transmitted, or by writing data when
BTF is set, as in both cases the data register is still empty.
Bit 6 RxNE: Data register not empty (receivers)
0: Data register empty
1: Data register not empty
– Set when data register is not empty in receiver mode. RxNE is not set during address phase.
– Cleared by software reading or writing the DR register or by hardware when PE=0.
RxNE is not set in case of ARLO event.
Note: RxNE is not cleared by reading data when BTF is set, as the data register is still full.

Bit 2 BTF: Byte transfer finished
0: Data byte transfer not done
1: Data byte transfer succeeded
– Set by hardware when NOSTRETCH=0 and:
– In reception when a new byte is received (including ACK pulse) and DR has not been read
yet (RxNE=1).
– In transmission when a new byte should be sent and DR has not been written yet (TxE=1).
– Cleared by software by either a read or write in the DR register or by hardware after a start or
a stop condition in transmission or when PE=0.
Note: The BTF bit is not set after a NACK reception
The BTF bit is not set if next byte to be transmitted is the PEC (TRA=1 in I2C_SR2
register and PEC=1 in I2C_CR1 register)

Bit 1 ADDR: Address sent (master mode)/matched (slave mode)
This bit is cleared by software reading SR1 register followed reading SR2, or by hardware
when PE=0.
Address matched (Slave)
0: Address mismatched or not received.
1: Received address matched.
– Set by hardware as soon as the received slave address matched with the OAR registers
content or a general call or a SMBus Device Default Address or SMBus Host or SMBus Alert
is recognized. (when enabled depending on configuration).
Note: In slave mode, it is recommended to perform the complete clearing sequence (READ
SR1 then READ SR2) after ADDR is set. Refer to Figure 242: Transfer sequence
diagram for slave receiver on page 848.
Address sent (Master)
0: No end of address transmission
1: End of address transmission
– For 10-bit addressing, the bit is set after the ACK of the 2nd byte.
– For 7-bit addressing, the bit is set after the ACK of the byte.
Note: ADDR is not set after a NACK reception

27.6.7 I2C Status register 2 (I2C_SR2)

Читаем о формировании последовательностей чтения/записи модулем i2c.

Запись.
http://s9.uploads.ru/KD4N8.png

Обозначения на рисунке:
S- Start
Sr-ReStart
P- Stop
A- ACK
NA- NACK
EVx - события

Описание событий.
EV5 : SB=1, сбрасывается чтением SR1 с последующей записью в DR адреса slave.
EV6 : ADDR=1, сбрасывается чтением SR1 с последующим  чтением SR2
EV8_1 : TXE=1, сдвиговый регистр пуст, буфер пуст, записываем данный в DR.
EV8 : TXE=1, сдвиговый регистр не пуст, буфер пуст,TXE сбрасывается записью в DR.
EV8_2 : TXE=1, BTF=1. Программа формирует Stop условие. TXE и BTF сбрасываются железом при Stop условии.

Исходя из описания последовательностей  описанных в даташите на eeprom и описания событий модуля I2C,
сформируем последовательность записи ячейки с  произвольным адресом:

1. Формируем Start.
 

Код:
I2C1->CR1 |= I2C_CR1_START;

2. Обрабатываем EV5   
 

Код:
 while (!(I2C1->SR1 & I2C_SR1_SB)){};            
   (void) I2C1->SR1;

3. Передаем адрес slave

Код:
 I2C1->DR = EEPROM_ADDRESS;

4. Обрабатываем EV6        

Код:
 while (!(I2C1->SR1 & I2C_SR1_ADDR)){};	
  (void) I2C1->SR1;
  (void) I2C1->SR2;

5. Передаем старший байт адреса
   

Код:
 I2C1->DR=(uint8_t) (address>>8);

6. Ждем освобождения буфера
   

Код:
while (!(I2C1->SR1 & I2C_SR1_TXE)){};

7. Передаем младший байт адреса
   

Код:
  I2C1->DR=(uint8_t)(address &0x00FF);

8. Ждем освобождения буфера

Код:
while (!(I2C1->SR1 & I2C_SR1_TXE)){};

9. Передаем данные для ячейки памяти
   

Код:
 I2C1->DR=data;

10. Ждем окончания передачи
   

Код:
 while (!(I2C1->SR1 & I2C_SR1_BTF)){};

11. Формируем Stop
 

Код:
 I2C1->CR1 |= I2C_CR1_STOP;

Чтение.
http://sa.uploads.ru/KOIXi.png

Описание событий.
EV5 : SB=1, сбрасывается чтением SR1 с последующей записью в DR адреса slave.
EV6 : ADDR=1, сбрасывается чтением SR1 с последующим  чтением SR2
EV7 : RxNE=1, сбрасывается чтением DR.
EV7_1 : RxNE=1,сбрасывается чтением DR. Для  NACK сбрасываем ACK и формируем Stop.

Сформируем чтение  ячейки eeprom c произвольного адреса.
Как видим из Figure 6-2 Random Read даташита на 24LC64.
Последовательность задания адреса ячейки полностью идентична той что в процедуре записи.

                       

Код:
 I2C1->CR1 |= I2C_CR1_START;    	
    	while (!(I2C1->SR1 & I2C_SR1_SB)){};            
    	(void) I2C1->SR1;
     
    	I2C1->DR = EEPROM_ADDRESS;           	
    	while (!(I2C1->SR1 & I2C_SR1_ADDR)){};	
    	(void) I2C1->SR1;
    	(void) I2C1->SR2;

    	I2C1->DR=(uint8_t) (address>>8);
    	while (!(I2C1->SR1 & I2C_SR1_TXE)){};
    	I2C1->DR=(uint8_t)(address &0x00FF);
    	while (!(I2C1->SR1 & I2C_SR1_TXE)){};

Теперь само чтение :
1. Формируем Restart.

Код:
 I2C1->CR1 |= I2C_CR1_START;

2. Обрабатываем EV5   

Код:
while (!(I2C1->SR1 & I2C_SR1_SB)){};            
(void) I2C1->SR1;

3. Инициируем чтение, посылая адрес+ R/#W в единице.    

Код:
I2C1->DR = EEPROM_ADDRESS|0x01;

4.Обрабатываем EV6   

Код:
while (!(I2C1->SR1 & I2C_SR1_ADDR)){};	
(void) I2C1->SR1;
(void) I2C1->SR2;

5. Формируем NACK

Код:
I2C1->CR1 &= ~I2C_CR1_ACK;

6. Ждем окончания приема данных

Код:
while (!(I2C1->SR1 & I2C_SR1_RXNE)){};

7.Вычитываем данные из буфера

Код:
 eeprom_data = I2C1->DR;

8.Формируем Stop     

Код:
I2C1->CR1 |= I2C_CR1_STOP;
Код:
void 	i2c_eeprom_write(uint16_t address, uint8_t data)
    {


	    I2C1->CR1 |= I2C_CR1_START;    	
    	while (!(I2C1->SR1 & I2C_SR1_SB)){};            
    	(void) I2C1->SR1;
     
    	I2C1->DR = EEPROM_ADDRESS;           	
    	while (!(I2C1->SR1 & I2C_SR1_ADDR)){};	
    	(void) I2C1->SR1;
    	(void) I2C1->SR2;

    	I2C1->DR=(uint8_t) (address>>8);
    	while (!(I2C1->SR1 & I2C_SR1_TXE)){};
    	I2C1->DR=(uint8_t)(address &0x00FF);
    	while (!(I2C1->SR1 & I2C_SR1_TXE)){};    
        
    	I2C1->DR=data;    
      while (!(I2C1->SR1 & I2C_SR1_BTF)){};
	
      I2C1->CR1 |= I2C_CR1_STOP;
    
    
    }
    
uint8_t 	i2c_eeprom_read(uint16_t address)
{
      uint8_t eeprom_data;
      I2C1->CR1 |= I2C_CR1_START;    	
    	while (!(I2C1->SR1 & I2C_SR1_SB)){};            
    	(void) I2C1->SR1;
     
    	I2C1->DR = EEPROM_ADDRESS;           	
    	while (!(I2C1->SR1 & I2C_SR1_ADDR)){};	
    	(void) I2C1->SR1;
    	(void) I2C1->SR2;

    	I2C1->DR=(uint8_t) (address>>8);
    	while (!(I2C1->SR1 & I2C_SR1_TXE)){};
    	I2C1->DR=(uint8_t)(address &0x00FF);
    	while (!(I2C1->SR1 & I2C_SR1_TXE)){};    

    	I2C1->CR1 |= I2C_CR1_START;    	
    	while (!(I2C1->SR1 & I2C_SR1_SB)){};            
    	(void) I2C1->SR1;
     
    	I2C1->DR = EEPROM_ADDRESS|0x01;           	
    	while (!(I2C1->SR1 & I2C_SR1_ADDR)){};	
    	(void) I2C1->SR1;
    	(void) I2C1->SR2;	
        
    	I2C1->CR1 &= ~I2C_CR1_ACK;	
    	while (!(I2C1->SR1 & I2C_SR1_RXNE)){};
      eeprom_data = I2C1->DR;
      I2C1->CR1 |= I2C_CR1_STOP; 
	    return eeprom_data;
   }

Запись .
http://sh.uploads.ru/OfmZC.png

Чтение.

http://s9.uploads.ru/WJM4c.png

4

Дополнил.

5

Последовательное чтение блока ячеек.

http://s5.uploads.ru/CBo5I.png

Sequential Read
Sequential reads are initiated in the same way as a random read except that after the 24XX64 transmits the
first data byte, the master issues an acknowledge as
opposed to the STOP condition used in a random read.
This acknowledge directs the 24XX64 to transmit the
next sequentially addressed 8-bit word (Figure 6-3).
Following the final byte transmitted to the master, the
master will NOT generate an acknowledge but will generate a STOP condition. To provide sequential reads
the 24XX64 contains an internal address pointer which
is incremented by one at the completion of each operation. This address pointer allows the entire memory
contents to be serially read during one operation. The
internal address pointer will automatically roll over from
address 1FFF to address 0000 if the master acknowledges the byte received from the array address 1FFF

Последовательное чтение инициируется аналогично произвольному,
за исключением того, что после передачи eeprom 24xx64 первого байта данных, master генерирует ACK вместо NACK и Stop.
Это заставляет eeprom передавать данные следующей ячейки памяти.
  После передачи последнего байта master должен завершить передачу выставив NACK и сформировав Stop.
  При последовательном чтении внутренний указатель адреса eeprom инкрементируется при каждой завершенной операции чтения.
  Указатель адреса автоматически прокручивается при достижении адреса 0x1FFF в адрес 0x0000 .

6

Ты бы еще под F0 так же расписал. Уже час сижу с RM воюю...

7

Ок. Сделаю.

8

Есть мелкий вопрос.

На этапе обработки EV6:

Код:
 
while (!(I2C1->SR1 & I2C_SR1_ADDR)){};	
(void) I2C1->SR1;
(void) I2C1->SR2;

Насколько критично после проверки в цикле еще раз считывать регистр I2C1->SR1?
Мы же этот регистр так и так при проверке условия выхода из цикла считываем.

9

Скорей всего да лишнее.

10

Как вам статья по I2C http://catethysis.ru/stm32_i2c/
Пытался разобраться в этом калокубе,тока больше запутался :) Время тока потерял )))

Отредактировано CERGEI (2017-09-17 11:30:39)

11

dosikus а у тебя какие константы для дисплея ssd1306 ? Добрался до запуска его ))

SSD1306_DISPLAY_OFF
SSD1306_SET_DISPLAY_CLOCK_DIV
SSD1306_SET_MULTIPLEX_RATIO
SSD1306_SET_DISPLAY_OFFSET
SSD1306_SET_START_LINE
SSD1306_SET_CHARGE_PUMP
SSD1306_MEMORY_ADDRESS_MODE
SSD1306_SET_LCOL_START_ADDRESS
SSD1306_SEGMENT_REMAP
SSD1306_COM_SCAN_INVERSE
SSD1306_SET_COM_PINS_CONFIG
SSD1306_SET_CONTRAST
SSD1306_SET_PRECHARGE_PERIOD
SSD1306_SET_VCOM_DESELECT_LVL
SSD1306_ENTIRE_DISPLAY_RESUME
SSD1306_NORMAL_DISPLAY
SSD1306_DISPLAY_ON

12

Код:
#define SSD1306_ADDRESS                0x3C
#define SSD1306_WIDTH                  128
#define SSD1306_HEIGHT                 64
#define SSD1306_COMMAND_MODE           0x00
#define SSD1306_DATA_MODE              0x40

// Команды
#define SSD1306_SET_CONTRAST           0x81
#define SSD1306_ENTIRE_DISPLAY_RESUME  0xA4
#define SSD1306_ENTIRE_DISPLAY_ON      0xA5
#define SSD1306_NORMAL_DISPLAY         0xA6
#define SSD1306_INVERSE_DISPLAY        0xA7
#define SSD1306_DISPLAY_OFF            0xAE
#define SSD1306_DISPLAY_ON             0xAF
#define SSD1306_SET_LCOL_START_ADDRESS 0x00
#define SSD1306_SET_HCOL_START_ADDRESS 0x10
#define SSD1306_MEMORY_ADDRESS_MODE    0x20
#define SSD1306_SET_COLUMN_ADDRESS     0x21
#define SSD1306_SET_PAGE_ADDRESS       0x22
#define SSD1306_SET_PAGE_START_ADDRESS 0xB0
#define SSD1306_SET_START_LINE         0x40
#define SSD1306_SEGMENT_REMAP          0xA0
#define SSD1306_SET_MULTIPLEX_RATIO    0xA8
#define SSD1306_COM_SCAN_NORMAL        0xC0
#define SSD1306_COM_SCAN_INVERSE       0xC8
#define SSD1306_SET_DISPLAY_OFFSET     0xD3
#define SSD1306_SET_COM_PINS_CONFIG    0xDA
#define SSD1306_SET_DISPLAY_CLOCK_DIV  0xD5
#define SSD1306_SET_PRECHARGE_PERIOD   0xD9
#define SSD1306_SET_VCOM_DESELECT_LVL  0xDB
#define SSD1306_NOP                    0xE3
#define SSD1306_SET_CHARGE_PUMP        0x8D

// Режимы скролирования
#define SSD1306_RIGHT_SCROLL_SETUP      0x26
#define SSD1306_LEFT_SCROLL_SETUP       0x27
#define SSD1306_VERT_RIGHT_SCROLL_SETUP 0x29
#define SSD1306_VERT_LEFT_SCROLL_SETUP  0x2A
#define SSD1306_ACTIVATE_SCROLL         0x2F
#define SSD1306_DEACTIVATE_SCROLL       0x2E
#define SSD1306_SET_VERT_SCROLL_AREA    0xA3

// Скорости скролирования (меньше - быстрее)
#define SSD1306_2_FRAMES   0x07
#define SSD1306_3_FRAMES   0x04
#define SSD1306_4_FRAMES   0x05
#define SSD1306_5_FRAMES   0x00
#define SSD1306_25_FRAMES  0x06
#define SSD1306_64_FRAMES  0x01
#define SSD1306_128_FRAMES 0x02
#define SSD1306_256_FRAMES 0x03

13

Спасибо,самый толковый форум получился. :flag:

Пример настройки частот i2c на stm32f103c8t6 при тактировании HSE=8 Мгц и HCLK=72 Мгц
Проверял на LM75A.

http://s3.uploads.ru/t/3BnkG.png

http://s6.uploads.ru/t/USkFs.png

Отредактировано CERGEI (2017-10-09 05:27:22)

14

Пытаюсь реализовать работу с i2c на прерываниях. В целом работает, но есть непонятные глюки. Стоит выложить сюда свой код для обсуждения?

15

Так говоришь будто это нам нужно :)   Как это на прерываниях,дрыганьем + таймером ? или аппаратно с прерываниями?

16

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

17

Если без SPL и калокуба, всегда пожалуйста.
  "Велосипедов" на г.либах в сети и так предостаточно.

18

stm32f429+stmpe811

структуры
Код:
typedef enum I2C_PHASE_TypeDef{
	PHASE_DISABLE = 0,
	PHASE_START,
	PHASE_SLAVE_ADDR_W,
	PHASE_REG_ADDR_DATA_TX,
	PHASE_RESART,
	PHASE_SLAVE_ADDR_R,
	PHASE_RECEIVE,
	PHASE_END_TRANSMIT,
}I2C_PHASE_TypeDef;

typedef enum I2C_Direction_TypeDef{
	dir_rx = 0,
	dir_tx,
}I2C_Direction_TypeDef;

typedef struct STMPE811_IT_TypeDef{
	I2C_Direction_TypeDef i2c_direction;
	I2C_PHASE_TypeDef i2c_state;
	uint8_t slave_addr;        	//!< адрес slave, который используется в обработчике прерывания для обмена
	uint8_t reg_addr;        	//!< адрес регистра, который используется в обработчике прерывания для обмена
	uint32_t data_sz;        	//!< сколько осталось принять байт
	uint8_t* pData;            //!< куда складывать следующий принятый байт
}STMPE811_IT_TypeDef;

static STMPE811_IT_TypeDef exchange_state = {0}; //структура для хранения текущего состояния
#define __TS_I2C	I2C1
функция запуска обмена
Код:
static int8_t STMPE811_start_IT(uint8_t slave_addr, uint8_t reg_addr, uint8_t* pData, uint8_t data_sz, I2C_Direction_TypeDef direction){
	if(data_sz == 0 || exchange_state.i2c_state != PHASE_DISABLE){return -1;}	//выхожу, если обмен уже идет или размер равен 0

	//заполняю структуру, которая будет использоваться в обработчике прерываний
	exchange_state.slave_addr = slave_addr;
	exchange_state.reg_addr = reg_addr;
	exchange_state.data_sz = data_sz;
	exchange_state.pData = pData;
	exchange_state.i2c_direction = direction;
	exchange_state.i2c_state = PHASE_START;

	__TS_I2C->CR1 |= I2C_CR1_PE;        //enable

	//включаю прерывания
	//Tx Rx пока не включаю, чтобы от них не было событий. (возникает много обытий, источник не нашел, подозреваю Tx и Rx)
	__TS_I2C->CR2 |= I2C_CR2_ITEVTEN|I2C_CR2_ITERREN;

	//ACK по дефолту выключен (и выключается при выкл. периферии PE=0), включаю для размера больше 1
	if(data_sz>=2){ __TS_I2C->CR1 |= I2C_CR1_ACK;}

	__TS_I2C->CR1 |= I2C_CR1_START;        //формирование сигнала СТАРТ, остальное в обработчике прерывний

	return 0;
}
Обработчик событий
Код:
void __TS_I2C_EV_IRQHandler(void){
	uint16_t sr1, sr2;
	sr1 = __TS_I2C->SR1;
//	sr2 = __TS_I2C->SR2;
	//общая часть для RX и TX: СТАРТ->адрес slave с пометкой записи -> адрес регистра
	if((PHASE_START == exchange_state.i2c_state) && (sr1 & I2C_SR1_SB)){
    //чтобы сбросился бит SB нужно прочитать регистр SR1, а потом записать данные в DR
    exchange_state.i2c_state = PHASE_SLAVE_ADDR_W;
//    (void) __TS_I2C->SR1; уже прочитан
    __TS_I2C->DR = exchange_state.slave_addr|I2C_SEND_W;    //отправляю адрес slave c пометкой записи, чтобы следующим байтом передать адрес регистра

	}else if((PHASE_SLAVE_ADDR_W == exchange_state.i2c_state) && (sr1 & I2C_SR1_ADDR)){
    //чтобы сбросился бит ADDR, нужно последовательно прочитать регистры SR1, SR2
    exchange_state.i2c_state = PHASE_REG_ADDR_DATA_TX;
//    (void) __TS_I2C->SR1; 	//уже прочитан
    (void) __TS_I2C->SR2;
    if(DIR_TX == exchange_state.i2c_direction){ __TS_I2C->CR2 |= I2C_CR2_ITBUFEN;}//если запущена передача, включаю Rx Tx прерывания, т.к. будет отслеживаться флаг TxE
    __TS_I2C->DR = exchange_state.reg_addr;        	//передаю адрес регистра, для записи/чтения в/из него При этом флаг TxE сбросится

	}else if(DIR_RX == exchange_state.i2c_direction){	//обработчик для приема данных
    if((PHASE_REG_ADDR_DATA_TX == exchange_state.i2c_state) && (sr1 & I2C_SR1_BTF)){
    	//BTF очищается при чтении или записи в DR. жду именно его, чтобы РЕСТАРТ не перебил передачу
    	exchange_state.i2c_state = PHASE_RESART;
    	(void)__TS_I2C->DR;
    	__TS_I2C->CR1 |= I2C_CR1_START;        //рестарт

    }else if((PHASE_RESART == exchange_state.i2c_state) && (sr1 & I2C_SR1_SB)){
    	//чтобы сбросился бит SB нужно прочитать регистр SR1, а потом записать данные в DR
    	exchange_state.i2c_state = PHASE_SLAVE_ADDR_R;
//    	(void) __TS_I2C->SR1; уже прочитан
    	__TS_I2C->CR2 |= I2C_CR2_ITBUFEN;	//После прерывания ADDR автоматом будет принят байт данных. Тут вкл. прерывание, чтобы отловить RxNE для этого байта
    	__TS_I2C->DR = exchange_state.slave_addr|I2C_SEND_R;    //отправляю на шину адрес slave c пометкой чтения

    }else if((PHASE_SLAVE_ADDR_R == exchange_state.i2c_state) && (sr1 & I2C_SR1_ADDR)){
    	//чтобы сбросился бит ADDR, нужно последовательно прочитать регистры SR1, SR2
    	exchange_state.i2c_state = PHASE_RECEIVE;
//    	(void) __TS_I2C->SR1;  уже прочитан
    	(void) __TS_I2C->SR2;

    }else if((PHASE_RECEIVE == exchange_state.i2c_state) && (sr1 & I2C_SR1_RXNE)){
    	//RXNE очищается при чтении DR или при PE->0
    	if(exchange_state.data_sz > 2){
        *exchange_state.pData++ = __TS_I2C->DR; exchange_state.data_sz--;	//читаю байт и жду следующего (взведение флага RXNE)
    	}else if(exchange_state.data_sz == 2){
        __TS_I2C->CR1 &= ~I2C_CR1_ACK;                    //за один байт до конца выключаю ACK
        *exchange_state.pData++ = __TS_I2C->DR; exchange_state.data_sz--;
    	}else if(exchange_state.data_sz == 1){
        __TS_I2C->CR1 |= I2C_CR1_STOP;
        *exchange_state.pData++ = __TS_I2C->DR; exchange_state.data_sz--;
    	}

    	if(exchange_state.data_sz == 0){
        __TS_I2C->CR1 &= ~I2C_CR1_PE;        //периферия сама дождется окончания текущего трансфера и выключится при переходе в Idle, дополнительно ждать и следить за этим не нужно
        //!Успеет ли отработать СТОП? Idle считается после окончания передачи байт или после СТОП?
        __TS_I2C->CR2 &= ~(I2C_CR2_ITEVTEN|I2C_CR2_ITBUFEN|I2C_CR2_ITERREN);
        exchange_state.i2c_state = PHASE_DISABLE;

        //если был запущен прием данных с использованием структуры sRx
        if(TYPE_RX_NONE != sRx.state){
        	dbg_data_sr[dbg_data_cnt] = 13;
        	//выставляю флаг наличия новых данных
        	sRx.end_state = END_RX;
        }
    	}
    }
	}else if(DIR_TX == exchange_state.i2c_direction){
    if((PHASE_REG_ADDR_DATA_TX == exchange_state.i2c_state) && (sr1 & I2C_SR1_TXE)){
    	//TXE очищается при записи в DR или после START/STOR или при PE->0
    	if(exchange_state.data_sz > 0){
        __TS_I2C->DR = *exchange_state.pData;    //отправляю байт данных
        exchange_state.pData++;
        exchange_state.data_sz--;
    	}else{
        //в этом месте условия сброса флага TxE не выполняются и он может выдавать лишние прерывания. Отключаю их
        __TS_I2C->CR2 &= ~I2C_CR2_ITBUFEN;
        //нечего передавать, жду флаг BTF
        exchange_state.i2c_state = PHASE_END_TRANSMIT;
    	}

    }else if((PHASE_END_TRANSMIT == exchange_state.i2c_state) && (sr1 & I2C_SR1_BTF)){
    	//BTF очищается при чтении или записи в DR
    	exchange_state.i2c_state = PHASE_DISABLE;
    	(void)__TS_I2C->DR;
    	__TS_I2C->CR1 |= I2C_CR1_STOP;
    	__TS_I2C->CR1 &= ~I2C_CR1_PE;        //периферия сама дождется окончания текущего трансфера и выключится при переходе в Idle, дополнительно ждать и следить за этим не нужно
    	//!Успеет ли отработать СТОП? Idle считается после окончания передачи байт или после СТОП?
    	__TS_I2C->CR2 &= ~(I2C_CR2_ITEVTEN|I2C_CR2_ITBUFEN|I2C_CR2_ITERREN);
    	//если была запущена передача данных с использованием структуры sRx
    	if(TYPE_RX_NONE != sRx.state){
        //выставляю флаг наличия новых данных
        sRx.end_state = END_TX;
    	}
    }
	}
}

обновил код - вынес чтение регистра SR1 в начало обработчика, чтобы он читался только один раз

постарался все объяснить в коде и комментариях. тут неудачное форматирование, лучше во внешнем редакторе смотреть
Вкратце суть такая: создаются переменные, в которых хранится текущее состояние обмена - фаза. Функция старта обмена заполняет внутренние переменные и генерит СТАРТ. В обработчике прерываний выполняется код в зависимости от фазы обмена.
Для приема это СТАРТ->slave адрес+W->адрес регистра->реСТАРТ->slave адрес+R->прием данных->СТОП
Для передачи СТАРТ->slave адрес+W->адрес регистра->передача данных->СТОП

Возникающих глюк виден на рисунке. После СТАРТ сразу генерится СТОП.
http://s1.uploads.ru/t/5x1BN.png
http://s4.uploads.ru/t/v3NY1.png

Пока причесывал код, обратил внимание на заметку в RM:

When the STOP, START or PEC bit is set, the software must not perform any write access to I2C_CR1 before this bit is cleared by hardware. Otherwise there is a risk of setting a second STOP, START or PEC request.

Отредактировано van_de_luxe (2017-10-25 16:05:21)

19

Пишу свой драйвер на ssd1306 на вывод строки.
Хотел узнать по скролированию.Попробую понять по дашифту :)  Для чего и как применять и скорость скролирования на что влияет:

Код:
// Режимы скролирования
#define SSD1306_RIGHT_SCROLL_SETUP      0x26
#define SSD1306_LEFT_SCROLL_SETUP       0x27
#define SSD1306_VERT_RIGHT_SCROLL_SETUP 0x29
#define SSD1306_VERT_LEFT_SCROLL_SETUP  0x2A
#define SSD1306_ACTIVATE_SCROLL         0x2F
#define SSD1306_DEACTIVATE_SCROLL       0x2E
#define SSD1306_SET_VERT_SCROLL_AREA    0xA3

// Скорости скролирования (меньше - быстрее)
#define SSD1306_2_FRAMES   0x07
#define SSD1306_3_FRAMES   0x04
#define SSD1306_4_FRAMES   0x05
#define SSD1306_5_FRAMES   0x00
#define SSD1306_25_FRAMES  0x06
#define SSD1306_64_FRAMES  0x01
#define SSD1306_128_FRAMES 0x02
#define SSD1306_256_FRAMES 0x03

20

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

21

Так и не победил DMA и i2c на 103c8t6. Отправляю одномерным уже массивом- первый байт не передает просто из 128, также после минуты работы i2c просто зависает на отправке адреса.

22

dosikus написал(а):

16*CCR * TPCLK1 + 9*CCR * TPCLK1=1900nS

Уважаемый автор, поправьте, пожалуйста, строчку, я голову сломал, откуда такая цифра 1900... :)
Все равно в расчетах используется правильная - 2500 нс.

23

Выручайте, борюсь с I2C на STM32F103C8 и никак. Пытаюсь подключить DS3231.
Инициализация:

Код:
void I2C_Configure(void)
{
    RCC->APB2ENR |= RCC_APB2ENR_AFIOEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_IOPCEN;
    GPIOB->CRL |= GPIO_CRL_MODE6 | GPIO_CRL_MODE7 | GPIO_CRL_CNF6 | GPIO_CRL_CNF7;
    for(uint8_t i = 0; i < 150; i++) {__NOP();};
    RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
    I2C1->CR1 |= I2C_CR1_SWRST;
    I2C1->CR1 &= ~I2C_CR1_SWRST;
    I2C1->CR2 &= ~I2C_CR2_FREQ;
    I2C1->CR2 |= 36;                 //APB1 = 36MHz 
    I2C1->CCR &= ~I2C_CCR_CCR;
    I2C1->CCR = 180;                 //(36MHz/100KHz/2)
    I2C1->TRISE = 37;                //(1mcs/(1/36MHz)+1)
    I2C1->CR1 |= I2C_CR1_PE;
    for(uint8_t i = 0; i < 255; i++) {__NOP();};
}

Запись (пока только один байт):

Код:
void I2C_WriteData(I2C_TypeDef *I2C, uint16_t DeviceAddress, uint8_t *Data, uint8_t Length)
{
    I2C->CR1 |= I2C_CR1_ACK;
    I2C->CR1 |= I2C_CR1_PE;
    I2C->CR1 |= I2C_CR1_START;
    
    while (!(I2C->SR1 & I2C_SR1_SB)){__NOP();};
    (void)I2C->SR1;
    // send device address
    I2C->DR = DeviceAddress;
    while (!(I2C->SR1 & I2C_SR1_ADDR)){__NOP();};
    (void)I2C->SR1;
    (void)I2C->SR2;
    I2C->DR = 0;
    while (!(I2C1->SR1 & I2C_SR1_BTF));
    I2C1->CR1 |= I2C_CR1_STOP;
}

В результате DS3231 не отвечает ACK и висим в цикле по I2C_SR1_ADDR:
http://s7.uploads.ru/t/TJ7Y0.png

То же самое но когда отправляет HAL:
http://sg.uploads.ru/t/F1kw5.png

Отредактировано alexsam (2018-02-16 23:47:08)

24

dosikus же выше всё подробнейше расписал
А по специфическим заморочкам в stm32f103 см AN2824

25

В том то и дело что я не понимаю в чем отличие. Если посмотреть на то что происходит на шине то видно что посылки от мастера совершенно идентичны. Но в первом случае слейв отвечает а во втором нет.

26

alexsam написал(а):

В том то и дело что я не понимаю в чем отличие. Если посмотреть на то что происходит на шине то видно что посылки от мастера совершенно идентичны. Но в первом случае слейв отвечает а во втором нет.

Старт во втором случае длиннее на картинках, может в этом дело

27

vt написал(а):

Старт во втором случае длиннее на картинках, может в этом дело

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

Можно мои посты удалить чтоб не засирали тему.
Всем спасибо.

28

Зачем же удалять??? Такие "шедевры" ХАЛа должны быть видны всем... ИМХО...

говнохал давит линию вниз там где должен быть ACK от девайса

29

У меня слов нет,кто эти тайминги придумал прописывать в stm32f030f4p6.
Расчет на 400 кГц не совпадает с помощью STM32F0-F3_AN4235_V1.0.1 и также с реферанса пример есть и тот тоже не совпадает по скорости........

30

В F0 и F7, где новые модули I2C расчет таймингов довольно-таки сложен.
Есть пример подобных расчетов на линух под F7, выдрать из экселовских файлов и калокуба не удалось увы...

код

/*
* Driver for STMicroelectronics STM32F7 I2C controller
*
* This I2C controller is described in the STM32F75xxx and STM32F74xxx Soc
* reference manual.
* Please see below a link to the documentation:
* http://www.st.com/resource/en/reference … 124865.pdf
*
* Copyright (C) M'boumba Cedric Madianga 2017
* Author: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
*
* This driver is based on i2c-stm32f4.c
*
* License terms:  GNU General Public License (GPL), version 2
*/
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/reset.h>
#include <linux/slab.h>

#include "i2c-stm32.h"

/* STM32F7 I2C registers */
#define STM32F7_I2C_CR1        0x00
#define STM32F7_I2C_CR2        0x04
#define STM32F7_I2C_TIMINGR    0x10
#define STM32F7_I2C_ISR        0x18
#define STM32F7_I2C_ICR        0x1C
#define STM32F7_I2C_RXDR    0x24
#define STM32F7_I2C_TXDR    0x28

/* STM32F7 I2C control 1 */
#define STM32F7_I2C_CR1_ANFOFF    BIT(12)
#define STM32F7_I2C_CR1_ERRIE    BIT(7)
#define STM32F7_I2C_CR1_TCIE    BIT(6)
#define STM32F7_I2C_CR1_STOPIE    BIT(5)
#define STM32F7_I2C_CR1_NACKIE    BIT(4)
#define STM32F7_I2C_CR1_ADDRIE    BIT(3)
#define STM32F7_I2C_CR1_RXIE    BIT(2)
#define STM32F7_I2C_CR1_TXIE    BIT(1)
#define STM32F7_I2C_CR1_PE    BIT(0)
#define STM32F7_I2C_ALL_IRQ_MASK    (STM32F7_I2C_CR1_ERRIE \
            | STM32F7_I2C_CR1_TCIE \
            | STM32F7_I2C_CR1_STOPIE \
            | STM32F7_I2C_CR1_NACKIE \
            | STM32F7_I2C_CR1_RXIE \
            | STM32F7_I2C_CR1_TXIE)

/* STM32F7 I2C control 2 */
#define STM32F7_I2C_CR2_RELOAD    BIT(24)
#define STM32F7_I2C_CR2_NBYTES_MASK    GENMASK(23, 16)
#define STM32F7_I2C_CR2_NBYTES(n)    (((n) & 0xff) << 16)
#define STM32F7_I2C_CR2_NACK    BIT(15)
#define STM32F7_I2C_CR2_STOP    BIT(14)
#define STM32F7_I2C_CR2_START    BIT(13)
#define STM32F7_I2C_CR2_RD_WRN    BIT(10)
#define STM32F7_I2C_CR2_SADD7_MASK    GENMASK(7, 1)
#define STM32F7_I2C_CR2_SADD7(n)    (((n) & 0x7f) << 1)

/* STM32F7 I2C Interrupt Status */
#define STM32F7_I2C_ISR_BUSY    BIT(15)
#define STM32F7_I2C_ISR_ARLO    BIT(9)
#define STM32F7_I2C_ISR_BERR    BIT(8)
#define STM32F7_I2C_ISR_TCR    BIT(7)
#define STM32F7_I2C_ISR_TC    BIT(6)
#define STM32F7_I2C_ISR_STOPF    BIT(5)
#define STM32F7_I2C_ISR_NACKF    BIT(4)
#define STM32F7_I2C_ISR_RXNE    BIT(2)
#define STM32F7_I2C_ISR_TXIS    BIT(1)

/* STM32F7 I2C Interrupt Clear */
#define STM32F7_I2C_ICR_ARLOCF    BIT(9)
#define STM32F7_I2C_ICR_BERRCF    BIT(8)
#define STM32F7_I2C_ICR_STOPCF    BIT(5)
#define STM32F7_I2C_ICR_NACKCF    BIT(4)

/* STM32F7 I2C Timing */
#define STM32F7_I2C_TIMINGR_PRESC(n)    (((n) & 0xf) << 28)
#define STM32F7_I2C_TIMINGR_SCLDEL(n)    (((n) & 0xf) << 20)
#define STM32F7_I2C_TIMINGR_SDADEL(n)    (((n) & 0xf) << 16)
#define STM32F7_I2C_TIMINGR_SCLH(n)    (((n) & 0xff) << 8)
#define STM32F7_I2C_TIMINGR_SCLL(n)    ((n) & 0xff)

#define STM32F7_I2C_MAX_LEN    0xff

#define STM32F7_I2C_DNF_DEFAULT    0
#define STM32F7_I2C_DNF_MAX    16

#define STM32F7_I2C_ANALOG_FILTER_ENABLE 1
#define STM32F7_I2C_ANALOG_FILTER_DELAY_MIN 50 /* ns */
#define STM32F7_I2C_ANALOG_FILTER_DELAY_MAX 260 /* ns */

#define STM32F7_I2C_RISE_TIME_DEFAULT    25 /* ns */
#define STM32F7_I2C_FALL_TIME_DEFAULT    10 /* ns */

#define STM32F7_PRESC_MAX    BIT(4)
#define STM32F7_SCLDEL_MAX    BIT(4)
#define STM32F7_SDADEL_MAX    BIT(4)
#define STM32F7_SCLH_MAX    BIT(8)
#define STM32F7_SCLL_MAX    BIT(8)

/**
* struct stm32f7_i2c_spec - private i2c specification timing
* @rate: I2C bus speed (Hz)
* @rate_min: 80% of I2C bus speed (Hz)
* @rate_max: 100% of I2C bus speed (Hz)
* @fall_max: Max fall time of both SDA and SCL signals (ns)
* @rise_max: Max rise time of both SDA and SCL signals (ns)
* @hddat_min: Min data hold time (ns)
* @vddat_max: Max data valid time (ns)
* @sudat_min: Min data setup time (ns)
* @l_min: Min low period of the SCL clock (ns)
* @h_min: Min high period of the SCL clock (ns)
*/
struct stm32f7_i2c_spec {
u32 rate;
u32 rate_min;
u32 rate_max;
u32 fall_max;
u32 rise_max;
u32 hddat_min;
u32 vddat_max;
u32 sudat_min;
u32 l_min;
u32 h_min;
};

/**
* struct stm32f7_i2c_setup - private I2C timing setup parameters
* @speed: I2C speed mode (standard, Fast Plus)
* @speed_freq: I2C speed frequency  (Hz)
* @clock_src: I2C clock source frequency (Hz)
* @rise_time: Rise time (ns)
* @fall_time: Fall time (ns)
* @dnf: Digital filter coefficient (0-16)
* @analog_filter: Analog filter delay (On/Off)
*/
struct stm32f7_i2c_setup {
enum stm32_i2c_speed speed;
u32 speed_freq;
u32 clock_src;
u32 rise_time;
u32 fall_time;
u8 dnf;
bool analog_filter;
};

/**
* struct stm32f7_i2c_timings - private I2C output parameters
* @prec: Prescaler value
* @scldel: Data setup time
* @sdadel: Data hold time
* @sclh: SCL high period (master mode)
* @sclh: SCL low period (master mode)
*/
struct stm32f7_i2c_timings {
struct list_head node;
u8 presc;
u8 scldel;
u8 sdadel;
u8 sclh;
u8 scll;
};

/**
* struct stm32f7_i2c_msg - client specific data
* @addr: 8-bit slave addr, including r/w bit
* @count: number of bytes to be transferred
* @buf: data buffer
* @result: result of the transfer
* @stop: last I2C msg to be sent, i.e. STOP to be generated
*/
struct stm32f7_i2c_msg {
u8 addr;
u32 count;
u8 *buf;
int result;
bool stop;
};

/**
* struct stm32f7_i2c_dev - private data of the controller
* @adap: I2C adapter for this controller
* @dev: device for this controller
* @base: virtual memory area
* @complete: completion of I2C message
* @clk: hw i2c clock
* @speed: I2C clock frequency of the controller. Standard, Fast or Fast+
* @msg: Pointer to data to be written
* @msg_num: number of I2C messages to be executed
* @msg_id: message identifiant
* @f7_msg: customized i2c msg for driver usage
* @setup: I2C timing input setup
* @timing: I2C computed timings
*/
struct stm32f7_i2c_dev {
struct i2c_adapter adap;
struct device *dev;
void __iomem *base;
struct completion complete;
struct clk *clk;
int speed;
struct i2c_msg *msg;
unsigned int msg_num;
unsigned int msg_id;
struct stm32f7_i2c_msg f7_msg;
struct stm32f7_i2c_setup *setup;
struct stm32f7_i2c_timings timing;
};

/**
* All these values are coming from I2C Specification, Version 6.0, 4th of
* April 2014.
*
* Table10. Characteristics of the SDA and SCL bus lines for Standard, Fast,
* and Fast-mode Plus I2C-bus devices
*/
static struct stm32f7_i2c_spec i2c_specs[] = {
[STM32_I2C_SPEED_STANDARD] = {
    .rate = 100000,
    .rate_min = 80000,
    .rate_max = 100000,
    .fall_max = 300,
    .rise_max = 1000,
    .hddat_min = 0,
    .vddat_max = 3450,
    .sudat_min = 250,
    .l_min = 4700,
    .h_min = 4000,
},
[STM32_I2C_SPEED_FAST] = {
    .rate = 400000,
    .rate_min = 320000,
    .rate_max = 400000,
    .fall_max = 300,
    .rise_max = 300,
    .hddat_min = 0,
    .vddat_max = 900,
    .sudat_min = 100,
    .l_min = 1300,
    .h_min = 600,
},
[STM32_I2C_SPEED_FAST_PLUS] = {
    .rate = 1000000,
    .rate_min = 800000,
    .rate_max = 1000000,
    .fall_max = 100,
    .rise_max = 120,
    .hddat_min = 0,
    .vddat_max = 450,
    .sudat_min = 50,
    .l_min = 500,
    .h_min = 260,
},
};

struct stm32f7_i2c_setup stm32f7_setup = {
.rise_time = STM32F7_I2C_RISE_TIME_DEFAULT,
.fall_time = STM32F7_I2C_FALL_TIME_DEFAULT,
.dnf = STM32F7_I2C_DNF_DEFAULT,
.analog_filter = STM32F7_I2C_ANALOG_FILTER_ENABLE,
};

static inline void stm32f7_i2c_set_bits(void __iomem *reg, u32 mask)
{
writel_relaxed(readl_relaxed(reg) | mask, reg);
}

static inline void stm32f7_i2c_clr_bits(void __iomem *reg, u32 mask)
{
writel_relaxed(readl_relaxed(reg) & ~mask, reg);
}

/*
unsigned int round_closest(unsigned int dividend, unsigned int divisor)
{
    return (dividend + (divisor / 2)) / divisor;
}
int divRoundClosest(const int n, const int d)
{
  return ((n < 0) ^ (d < 0)) ? ((n - d/2)/d) : ((n + d/2)/d);
}

#define DIV_ROUND_CLOSEST(n, d) ((((n) < 0) ^ ((d) < 0)) ? (((n) - (d)/2)/(d)) : (((n) + (d)/2)/(d)))

int divide(x, y)
{
   int a = (x -1)/y +1;

   return a;
}
#define DIVIDE_WITH_ROUND(N, D)  (((N) == 0) ? 0:(((N * 10)/D) + 5)/10)

#define DIVIDE_WITH_ROUND(N, D)  (N == 0) ? 0:(N - D/2)/D + 1;
#define DIVIDE_WITH_ROUND(N, D)  (((N) == 0) ? 0:(((N * 10)/D) + 5)/10)

#define CEIL(a, b) (((a) / (b)) + (((a) % (b)) > 0 ? 1 : 0))

#define MIN(a, b)  (((a) < (b)) ? (a) : (b))
#define MAX(a, b)  (((a) > (b)) ? (a) : (b))
#define ABS(a)     (((a) < 0) ? -(a) : (a))

#define DIV_ROUND_INT(n,d) ((((n) < 0) ^ ((d) < 0)) ? (((n) - (d)/2)/(d)) : (((n) + (d)/2)/(d)))
or if you work only with unsigned ints
#define DIV_ROUND_UINT(n,d) ((((n) + (d)/2)/(d)))

/ round-to-nearest with mid-value bias towards positive infinity
int div_nearest( int n, int d )
   {
   if (d<0) n*=-1, d*=-1;
   return (abs(n)+((d-(n<0?1:0))>>1))/d * ((n<0)?-1:+1);
   }

Safer C code (unless you have other methods of handling /0):

return (_divisor > 0) ? ((_dividend + (_divisor - 1)) / _divisor) : _dividend;

This doesn't handle the problems that occur from having an incorrect return value as a result of your invalid input data, of course.
*/

static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
              struct stm32f7_i2c_setup *setup,
              struct stm32f7_i2c_timings *output)
{
u32 p_prev = STM32F7_PRESC_MAX;
u32 i2cclk = DIV_ROUND_CLOSEST(NSEC_PER_SEC,
               setup->clock_src);
u32 i2cbus = DIV_ROUND_CLOSEST(NSEC_PER_SEC,
               setup->speed_freq);
u32 clk_error_prev = i2cbus;
u32 tsync;
u32 af_delay_min, af_delay_max;
u32 dnf_delay;
u32 clk_min, clk_max;
int sdadel_min, sdadel_max;
int scldel_min;
struct stm32f7_i2c_timings *v, *_v, *s;
struct list_head solutions;
u16 p, l, a, h;
int ret = 0;

if (setup->speed >= STM32_I2C_SPEED_END) {
    dev_err(i2c_dev->dev, "speed out of bound {%d/%d}\n",
    setup->speed, STM32_I2C_SPEED_END - 1);
    return -EINVAL;
}

if ((setup->rise_time > i2c_specs[setup->speed].rise_max) ||
    (setup->fall_time > i2c_specs[setup->speed].fall_max)) {
    dev_err(i2c_dev->dev,
    "timings out of bound Rise{%d>%d}/Fall{%d>%d}\n",
    setup->rise_time, i2c_specs[setup->speed].rise_max,
    setup->fall_time, i2c_specs[setup->speed].fall_max);
    return -EINVAL;
}

if (setup->dnf > STM32F7_I2C_DNF_MAX) {
    dev_err(i2c_dev->dev,
    "DNF out of bound %d/%d\n",
    setup->dnf, STM32F7_I2C_DNF_MAX);
    return -EINVAL;
}

if (setup->speed_freq > i2c_specs[setup->speed].rate) {
    dev_err(i2c_dev->dev, "ERROR: Freq {%d/%d}\n",
    setup->speed_freq, i2c_specs[setup->speed].rate);
    return -EINVAL;
}

/*  Analog and Digital Filters */
af_delay_min =
    (setup->analog_filter ?
     STM32F7_I2C_ANALOG_FILTER_DELAY_MIN : 0);
af_delay_max =
    (setup->analog_filter ?
     STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0);
dnf_delay = setup->dnf * i2cclk;

sdadel_min = setup->fall_time - i2c_specs[setup->speed].hddat_min -
    af_delay_min - (setup->dnf + 3) * i2cclk;

sdadel_max = i2c_specs[setup->speed].vddat_max - setup->rise_time -
    af_delay_max - (setup->dnf + 4) * i2cclk;

scldel_min = setup->rise_time + i2c_specs[setup->speed].sudat_min;

if (sdadel_min < 0)
    sdadel_min = 0;
if (sdadel_max < 0)
    sdadel_max = 0;

dev_dbg(i2c_dev->dev, "SDADEL(min/max): %i/%i, SCLDEL(Min): %i\n",
    sdadel_min, sdadel_max, scldel_min);

INIT_LIST_HEAD(&solutions);
/* Compute possible values for PRESC, SCLDEL and SDADEL */
for (p = 0; p < STM32F7_PRESC_MAX; p++) {
    for (l = 0; l < STM32F7_SCLDEL_MAX; l++) {
    u32 scldel = (l + 1) * (p + 1) * i2cclk;

    if (scldel < scldel_min)
        continue;

    for (a = 0; a < STM32F7_SDADEL_MAX; a++) {
        u32 sdadel = (a * (p + 1) + 1) * i2cclk;

        if (((sdadel >= sdadel_min) &&
             (sdadel <= sdadel_max)) &&
            (p != p_prev)) {
        v = kmalloc(sizeof(*v), GFP_KERNEL);
        if (!v) {
            ret = -ENOMEM;
            goto exit;
        }

        v->presc = p;
        v->scldel = l;
        v->sdadel = a;
        p_prev = p;

        list_add_tail(&v->node,
                  &solutions);
        }
    }
    }
}

if (list_empty(&solutions)) {
    dev_err(i2c_dev->dev, "no Prescaler solution\n");
    ret = -EPERM;
    goto exit;
}

tsync = af_delay_min + dnf_delay + (2 * i2cclk);
s = NULL;
clk_max = NSEC_PER_SEC / i2c_specs[setup->speed].rate_min;
clk_min = NSEC_PER_SEC / i2c_specs[setup->speed].rate_max;

/*
* Among Prescaler possibilities discovered above figures out SCL Low
* and High Period. Provided:
* - SCL Low Period has to be higher than SCL Clock Low Period
*   defined by I2C Specification. I2C Clock has to be lower than
*   (SCL Low Period - Analog/Digital filters) / 4.
* - SCL High Period has to be lower than SCL Clock High Period
*   defined by I2C Specification
* - I2C Clock has to be lower than SCL High Period
*/
list_for_each_entry(v, &solutions, node) {
    u32 prescaler = (v->presc + 1) * i2cclk;

    for (l = 0; l < STM32F7_SCLL_MAX; l++) {
    u32 tscl_l = (l + 1) * prescaler + tsync;

    if ((tscl_l < i2c_specs[setup->speed].l_min) ||
        (i2cclk >=
         ((tscl_l - af_delay_min - dnf_delay) / 4))) {
        continue;
    }

    for (h = 0; h < STM32F7_SCLH_MAX; h++) {
        u32 tscl_h = (h + 1) * prescaler + tsync;
        u32 tscl = tscl_l + tscl_h +
        setup->rise_time + setup->fall_time;

        if ((tscl >= clk_min) && (tscl <= clk_max) &&
            (tscl_h >= i2c_specs[setup->speed].h_min) &&
            (i2cclk < tscl_h)) {
        int clk_error = tscl - i2cbus;

        if (clk_error < 0)
            clk_error = -clk_error;

        if (clk_error < clk_error_prev) {
            clk_error_prev = clk_error;
            v->scll = l;
            v->sclh = h;
            s = v;
        }
        }
    }
    }
}

if (!s) {
    dev_err(i2c_dev->dev, "no solution at all\n");
    ret = -EPERM;
    goto exit;
}

output->presc = s->presc;
output->scldel = s->scldel;
output->sdadel = s->sdadel;
output->scll = s->scll;
output->sclh = s->sclh;

dev_dbg(i2c_dev->dev,
    "Presc: %i, scldel: %i, sdadel: %i, scll: %i, sclh: %i\n",
    output->presc,
    output->scldel, output->sdadel,
    output->scll, output->sclh);

exit:
/* Release list and memory */
list_for_each_entry_safe(v, _v, &solutions, node) {
    list_del(&v->node);
    kfree(v);
}

return ret;
}

static int stm32f7_i2c_setup_timing(struct stm32f7_i2c_dev *i2c_dev,
            struct stm32f7_i2c_setup *setup)
{
int ret = 0;

setup->speed = i2c_dev->speed;
setup->speed_freq = i2c_specs[setup->speed].rate;
setup->clock_src = clk_get_rate(i2c_dev->clk);

if (!setup->clock_src) {
    dev_err(i2c_dev->dev, "clock rate is 0\n");
    return -EINVAL;
}

do {
    ret = stm32f7_i2c_compute_timing(i2c_dev, setup,
             &i2c_dev->timing);
    if (ret) {
    dev_err(i2c_dev->dev,
        "failed to compute I2C timings.\n");
    if (i2c_dev->speed > STM32_I2C_SPEED_STANDARD) {
        i2c_dev->speed--;
        setup->speed = i2c_dev->speed;
        setup->speed_freq =
        i2c_specs[setup->speed].rate;
        dev_warn(i2c_dev->dev,
        "downgrade I2C Speed Freq to (%i)\n",
        i2c_specs[setup->speed].rate);
    } else {
        break;
    }
    }
} while (ret);

if (ret) {
    dev_err(i2c_dev->dev, "Impossible to compute I2C timings.\n");
    return ret;
}

dev_dbg(i2c_dev->dev, "I2C Speed(%i), Freq(%i), Clk Source(%i)\n",
    setup->speed, setup->speed_freq, setup->clock_src);
dev_dbg(i2c_dev->dev, "I2C Rise(%i) and Fall(%i) Time\n",
    setup->rise_time, setup->fall_time);
dev_dbg(i2c_dev->dev, "I2C Analog Filter(%s), DNF(%i)\n",
    (setup->analog_filter ? "On" : "Off"), setup->dnf);

return 0;
}

static void stm32f7_i2c_hw_config(struct stm32f7_i2c_dev *i2c_dev)
{
struct stm32f7_i2c_timings *t = &i2c_dev->timing;
u32 timing = 0;

/* Timing settings */
timing |= STM32F7_I2C_TIMINGR_PRESC(t->presc);
timing |= STM32F7_I2C_TIMINGR_SCLDEL(t->scldel);
timing |= STM32F7_I2C_TIMINGR_SDADEL(t->sdadel);
timing |= STM32F7_I2C_TIMINGR_SCLH(t->sclh);
timing |= STM32F7_I2C_TIMINGR_SCLL(t->scll);
writel_relaxed(timing, i2c_dev->base + STM32F7_I2C_TIMINGR);

/* Enable I2C */
if (i2c_dev->setup->analog_filter)
    stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1,
             STM32F7_I2C_CR1_ANFOFF);
else
    stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
             STM32F7_I2C_CR1_ANFOFF);
stm32f7_i2c_set_bits(i2c_dev->base + STM32F7_I2C_CR1,
         STM32F7_I2C_CR1_PE);
}

static void stm32f7_i2c_write_tx_data(struct stm32f7_i2c_dev *i2c_dev)
{
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
void __iomem *base = i2c_dev->base;

if (f7_msg->count) {
    writeb_relaxed(*f7_msg->buf++, base + STM32F7_I2C_TXDR);
    f7_msg->count--;
}
}

static void stm32f7_i2c_read_rx_data(struct stm32f7_i2c_dev *i2c_dev)
{
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
void __iomem *base = i2c_dev->base;

if (f7_msg->count) {
    *f7_msg->buf++ = readb_relaxed(base + STM32F7_I2C_RXDR);
    f7_msg->count--;
}
}

static void stm32f7_i2c_reload(struct stm32f7_i2c_dev *i2c_dev)
{
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
u32 cr2;

cr2 = readl_relaxed(i2c_dev->base + STM32F7_I2C_CR2);

cr2 &= ~STM32F7_I2C_CR2_NBYTES_MASK;
if (f7_msg->count > STM32F7_I2C_MAX_LEN) {
    cr2 |= STM32F7_I2C_CR2_NBYTES(STM32F7_I2C_MAX_LEN);
} else {
    cr2 &= ~STM32F7_I2C_CR2_RELOAD;
    cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count);
}

writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2);
}

static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev)
{
u32 status;
int ret;

ret = readl_relaxed_poll_timeout(i2c_dev->base + STM32F7_I2C_ISR,
        status,
        !(status & STM32F7_I2C_ISR_BUSY),
        10, 1000);
if (ret) {
    dev_dbg(i2c_dev->dev, "bus busy\n");
    ret = -EBUSY;
}

return ret;
}

static void stm32f7_i2c_xfer_msg(struct stm32f7_i2c_dev *i2c_dev,
         struct i2c_msg *msg)
{
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
void __iomem *base = i2c_dev->base;
u32 cr1, cr2;

f7_msg->addr = msg->addr;
f7_msg->buf = msg->buf;
f7_msg->count = msg->len;
f7_msg->result = 0;
f7_msg->stop = (i2c_dev->msg_id >= i2c_dev->msg_num - 1);

reinit_completion(&i2c_dev->complete);

cr1 = readl_relaxed(base + STM32F7_I2C_CR1);
cr2 = readl_relaxed(base + STM32F7_I2C_CR2);

/* Set transfer direction */
cr2 &= ~STM32F7_I2C_CR2_RD_WRN;
if (msg->flags & I2C_M_RD)
    cr2 |= STM32F7_I2C_CR2_RD_WRN;

/* Set slave address */
cr2 &= ~STM32F7_I2C_CR2_SADD7_MASK;
cr2 |= STM32F7_I2C_CR2_SADD7(f7_msg->addr);

/* Set nb bytes to transfer and reload if needed */
cr2 &= ~(STM32F7_I2C_CR2_NBYTES_MASK | STM32F7_I2C_CR2_RELOAD);
if (f7_msg->count > STM32F7_I2C_MAX_LEN) {
    cr2 |= STM32F7_I2C_CR2_NBYTES(STM32F7_I2C_MAX_LEN);
    cr2 |= STM32F7_I2C_CR2_RELOAD;
} else {
    cr2 |= STM32F7_I2C_CR2_NBYTES(f7_msg->count);
}

/* Enable NACK, STOP, error and transfer complete interrupts */
cr1 |= STM32F7_I2C_CR1_ERRIE | STM32F7_I2C_CR1_TCIE |
    STM32F7_I2C_CR1_STOPIE | STM32F7_I2C_CR1_NACKIE;

/* Clear TX/RX interrupt */
cr1 &= ~(STM32F7_I2C_CR1_RXIE | STM32F7_I2C_CR1_TXIE);

/* Enable RX/TX interrupt according to msg direction */
if (msg->flags & I2C_M_RD)
    cr1 |= STM32F7_I2C_CR1_RXIE;
else
    cr1 |= STM32F7_I2C_CR1_TXIE;

/* Configure Start/Repeated Start */
cr2 |= STM32F7_I2C_CR2_START;

/* Write configurations registers */
writel_relaxed(cr1, base + STM32F7_I2C_CR1);
writel_relaxed(cr2, base + STM32F7_I2C_CR2);
}

static void stm32f7_i2c_disable_irq(struct stm32f7_i2c_dev *i2c_dev, u32 mask)
{
stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, mask);
}

static irqreturn_t stm32f7_i2c_isr_event(int irq, void *data)
{
struct stm32f7_i2c_dev *i2c_dev = data;
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
void __iomem *base = i2c_dev->base;
u32 status, mask;

status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);

/* Tx empty */
if (status & STM32F7_I2C_ISR_TXIS)
    stm32f7_i2c_write_tx_data(i2c_dev);

/* RX not empty */
if (status & STM32F7_I2C_ISR_RXNE)
    stm32f7_i2c_read_rx_data(i2c_dev);

/* NACK received */
if (status & STM32F7_I2C_ISR_NACKF) {
    dev_dbg(i2c_dev->dev, "<%s>: Receive NACK\n", __func__);
    writel_relaxed(STM32F7_I2C_ICR_NACKCF, base + STM32F7_I2C_ICR);
    f7_msg->result = -ENXIO;
}

/* STOP detection flag */
if (status & STM32F7_I2C_ISR_STOPF) {
    /* Disable interrupts */
    stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_ALL_IRQ_MASK);

    /* Clear STOP flag */
    writel_relaxed(STM32F7_I2C_ICR_STOPCF, base + STM32F7_I2C_ICR);

    complete(&i2c_dev->complete);
}

/* Transfer complete */
if (status & STM32F7_I2C_ISR_TC) {
    if (f7_msg->stop) {
    mask = STM32F7_I2C_CR2_STOP;
    stm32f7_i2c_set_bits(base + STM32F7_I2C_CR2, mask);
    } else {
    i2c_dev->msg_id++;
    i2c_dev->msg++;
    stm32f7_i2c_xfer_msg(i2c_dev, i2c_dev->msg);
    }
}

/*
* Transfer Complete Reload: 255 data bytes have been transferred
* We have to prepare the I2C controller to transfer the remaining
* data.
*/
if (status & STM32F7_I2C_ISR_TCR)
    stm32f7_i2c_reload(i2c_dev);

return IRQ_HANDLED;
}

static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data)
{
struct stm32f7_i2c_dev *i2c_dev = data;
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
void __iomem *base = i2c_dev->base;
struct device *dev = i2c_dev->dev;
u32 status;

status = readl_relaxed(i2c_dev->base + STM32F7_I2C_ISR);

/* Bus error */
if (status & STM32F7_I2C_ISR_BERR) {
    dev_err(dev, "<%s>: Bus error\n", __func__);
    writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR);
    f7_msg->result = -EIO;
}

/* Arbitration loss */
if (status & STM32F7_I2C_ISR_ARLO) {
    dev_dbg(dev, "<%s>: Arbitration loss\n", __func__);
    writel_relaxed(STM32F7_I2C_ICR_ARLOCF, base + STM32F7_I2C_ICR);
    f7_msg->result = -EAGAIN;
}

stm32f7_i2c_disable_irq(i2c_dev, STM32F7_I2C_ALL_IRQ_MASK);

complete(&i2c_dev->complete);

return IRQ_HANDLED;
}

static int stm32f7_i2c_xfer(struct i2c_adapter *i2c_adap,
        struct i2c_msg msgs[], int num)
{
struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap);
struct stm32f7_i2c_msg *f7_msg = &i2c_dev->f7_msg;
unsigned long time_left;
int ret;

i2c_dev->msg = msgs;
i2c_dev->msg_num = num;
i2c_dev->msg_id = 0;

ret = clk_enable(i2c_dev->clk);
if (ret) {
    dev_err(i2c_dev->dev, "Failed to enable clock\n");
    return ret;
}

ret = stm32f7_i2c_wait_free_bus(i2c_dev);
if (ret)
    goto clk_free;

stm32f7_i2c_xfer_msg(i2c_dev, msgs);

time_left = wait_for_completion_timeout(&i2c_dev->complete,
            i2c_dev->adap.timeout);
ret = f7_msg->result;

if (!time_left) {
    dev_dbg(i2c_dev->dev, "Access to slave 0x%x timed out\n",
    i2c_dev->msg->addr);
    ret = -ETIMEDOUT;
}

clk_free:
clk_disable(i2c_dev->clk);

return (ret < 0) ? ret : num;
}

static u32 stm32f7_i2c_func(struct i2c_adapter *adap)
{
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}

static struct i2c_algorithm stm32f7_i2c_algo = {
.master_xfer = stm32f7_i2c_xfer,
.functionality = stm32f7_i2c_func,
};

static int stm32f7_i2c_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct stm32f7_i2c_dev *i2c_dev;
const struct stm32f7_i2c_setup *setup;
struct resource *res;
u32 irq_error, irq_event, clk_rate, rise_time, fall_time;
struct i2c_adapter *adap;
struct reset_control *rst;
int ret;

i2c_dev = devm_kzalloc(&pdev->dev, sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
    return -ENOMEM;

res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
i2c_dev->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(i2c_dev->base))
    return PTR_ERR(i2c_dev->base);

irq_event = irq_of_parse_and_map(np, 0);
if (!irq_event) {
    dev_err(&pdev->dev, "IRQ event missing or invalid\n");
    return -EINVAL;
}

irq_error = irq_of_parse_and_map(np, 1);
if (!irq_error) {
    dev_err(&pdev->dev, "IRQ error missing or invalid\n");
    return -EINVAL;
}

i2c_dev->clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(i2c_dev->clk)) {
    dev_err(&pdev->dev, "Error: Missing controller clock\n");
    return PTR_ERR(i2c_dev->clk);
}
ret = clk_prepare_enable(i2c_dev->clk);
if (ret) {
    dev_err(&pdev->dev, "Failed to prepare_enable clock\n");
    return ret;
}

i2c_dev->speed = STM32_I2C_SPEED_STANDARD;
ret = device_property_read_u32(&pdev->dev, "clock-frequency",
               &clk_rate);
if (!ret && clk_rate >= 1000000)
    i2c_dev->speed = STM32_I2C_SPEED_FAST_PLUS;
else if (!ret && clk_rate >= 400000)
    i2c_dev->speed = STM32_I2C_SPEED_FAST;
else if (!ret && clk_rate >= 100000)
    i2c_dev->speed = STM32_I2C_SPEED_STANDARD;

rst = devm_reset_control_get(&pdev->dev, NULL);
if (IS_ERR(rst)) {
    dev_err(&pdev->dev, "Error: Missing controller reset\n");
    ret = PTR_ERR(rst);
    goto clk_free;
}
reset_control_assert(rst);
udelay(2);
reset_control_deassert(rst);

i2c_dev->dev = &pdev->dev;

ret = devm_request_irq(&pdev->dev, irq_event, stm32f7_i2c_isr_event, 0,
           pdev->name, i2c_dev);
if (ret) {
    dev_err(&pdev->dev, "Failed to request irq event %i\n",
    irq_event);
    goto clk_free;
}

ret = devm_request_irq(&pdev->dev, irq_error, stm32f7_i2c_isr_error, 0,
           pdev->name, i2c_dev);
if (ret) {
    dev_err(&pdev->dev, "Failed to request irq error %i\n",
    irq_error);
    goto clk_free;
}

setup = of_device_get_match_data(&pdev->dev);
i2c_dev->setup->rise_time = setup->rise_time;
i2c_dev->setup->fall_time = setup->fall_time;
i2c_dev->setup->dnf = setup->dnf;
i2c_dev->setup->analog_filter = setup->analog_filter;

ret = device_property_read_u32(i2c_dev->dev, "i2c-scl-rising-time-ns",
               &rise_time);
if (!ret)
    i2c_dev->setup->rise_time = rise_time;

ret = device_property_read_u32(i2c_dev->dev, "i2c-scl-falling-time-ns",
               &fall_time);
if (!ret)
    i2c_dev->setup->fall_time = fall_time;

ret = stm32f7_i2c_setup_timing(i2c_dev, i2c_dev->setup);
if (ret)
    goto clk_free;

stm32f7_i2c_hw_config(i2c_dev);

adap = &i2c_dev->adap;
i2c_set_adapdata(adap, i2c_dev);
snprintf(adap->name, sizeof(adap->name), "STM32F7 I2C(%pa)",
     &res->start);
adap->owner = THIS_MODULE;
adap->timeout = 2 * HZ;
adap->retries = 3;
adap->algo = &stm32f7_i2c_algo;
adap->dev.parent = &pdev->dev;
adap->dev.of_node = pdev->dev.of_node;

init_completion(&i2c_dev->complete);

ret = i2c_add_adapter(adap);
if (ret)
    goto clk_free;

platform_set_drvdata(pdev, i2c_dev);

clk_disable(i2c_dev->clk);

dev_info(i2c_dev->dev, "STM32F7 I2C-%d bus adapter\n", adap->nr);

return 0;

clk_free:
clk_disable_unprepare(i2c_dev->clk);

return ret;
}

static int stm32f7_i2c_remove(struct platform_device *pdev)
{
struct stm32f7_i2c_dev *i2c_dev = platform_get_drvdata(pdev);

i2c_del_adapter(&i2c_dev->adap);

clk_unprepare(i2c_dev->clk);

return 0;
}

static const struct of_device_id stm32f7_i2c_match[] = {
{ .compatible = "st,stm32f7-i2c", .data = &stm32f7_setup},
{},
};
MODULE_DEVICE_TABLE(of, stm32f7_i2c_match);

static struct platform_driver stm32f7_i2c_driver = {
.driver = {
    .name = "stm32f7-i2c",
    .of_match_table = stm32f7_i2c_match,
},
.probe = stm32f7_i2c_probe,
.remove = stm32f7_i2c_remove,
};

module_platform_driver(stm32f7_i2c_driver);

MODULE_AUTHOR("M'boumba Cedric Madianga <cedric.madianga@gmail.com>");
MODULE_DESCRIPTION("STMicroelectronics STM32F7 I2C driver");
MODULE_LICENSE("GPL v2");


Вы здесь » Микроконтроллеры » Архив » STM32 I2C