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

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

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


Вы здесь » Микроконтроллеры » Популярное » 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

структуры
функция запуска обмена
Обработчик событий

обновил код - вынес чтение регистра 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, выдрать из экселовских файлов и калокуба не удалось увы...

код

Вы здесь » Микроконтроллеры » Популярное » STM32 I2C