Перейти к содержанию
    

STM32F103 лишний байт при чтении по I2C

Коллеги, помогите, пожалуйста, понять в чем проблема.

Читаю данные из EEPROM 24XX64.

При чтении последнего байта выдаю NACK.

После этого STOP.

Все читается как надо, только меня смущает наличие лишней транзакции чтения после чтения последнего байта (тоже с NACK).

Вот код и скриншот из лог. анализатора.

Работа с I2C:

// control_i2c.c

#include <stm32f10x.h>
#include <stm32f10x_i2c.h>

#include <stdint.h>
#include <stdbool.h>

#include "main.h"
#include "system.h"

#include "control_i2c.h"

//---------------------------------------------------------------------------
void I2C_INIT (void)
{
	// Настройка I2C
	I2C1->CR2 = (CPU_CLOCK_HZ / 2) / 1000000 ;
	I2C1->CCR = I2C_CCR_FS | I2C_CCR_DUTY | 4 ;
	I2C1->TRISE = 12 ;
	
	// Включение I2C
	I2C1->CR1 = I2C_CR1_PE ;
}

//---------------------------------------------------------------------------
void start_I2C (void)
{
	I2C1->CR1 |= I2C_CR1_START ;
	while ((I2C1->SR1 & I2C_SR1_SB) == 0) ;
}
//---------------------------------------------------------------------------
void stop_I2C (void)
{
	I2C1->CR1 |= I2C_CR1_STOP ;
	while ((I2C1->SR2 & I2C_SR2_BUSY) == 0) ;
}
//---------------------------------------------------------------------------
bool write_byte_I2C (uint8_t data)
{
	I2C1->DR = data ;
	
	while (true)
	{
		uint16_t SR1 = I2C1->SR1 ;
		
		if (SR1 & I2C_SR1_AF)
		{
			I2C1->SR1 &= ~I2C_SR1_AF ;
			
			stop_I2C () ;
			return false ;
		}
		
		if (SR1 & I2C_SR1_ADDR)
		{
			(void) I2C1->SR2 ;
			
			break ;
		}
		
		if (SR1 & I2C_SR1_BTF)
		{
			break ;
		}
	}
	
	return true ;
}
//---------------------------------------------------------------------------
uint8_t read_byte_I2C (bool ack)
{
	if (ack) 
		I2C1->CR1 |=  I2C_CR1_ACK ;
	else 
		I2C1->CR1 &= ~I2C_CR1_ACK ;
	
	while ((I2C1->SR1 & I2C_SR1_RXNE) == 0) ;
	
	return I2C1->DR ;
}
//---------------------------------------------------------------------------

Работа с EEPROM:

bool ReadFlashMM (uint8_t n_mm, uint16_t address, uint8_t* data, uint16_t size)
{
	uint8_t chip_a = MM_FLASH_I2C_ADDR + (n_mm << 1) ;
	
	// S
	start_I2C () ;
	
	// AD+W
	if (!write_byte_I2C(chip_a)) 
		return false ;
	
	// MA H
	if (!write_byte_I2C(HIBYTE(address))) 
		return false ;
	
	// MA L
	if (!write_byte_I2C(LOBYTE(address))) 
		return false ;
	
	// S
	start_I2C () ;
	
	// AD+R
	if (!write_byte_I2C(chip_a | 0x01)) 
		return false ;
	
	for (uint16_t i=0 ; i<size ; i++)
	{
		// DATA
		if (i == (size-1)) 
			data[i] = read_byte_I2C (false) ;
		else 
			data[i] = read_byte_I2C (true) ;
	}
	
	// P
	stop_I2C () ;
	
	return true ;
}

Скриншот из лог. анализатора:

1006338784_I2Cread.thumb.png.d7ddf16c97e69dcc7afc57005f6b920c.png

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Код я даже не смотрел, так как причина вполне очевидна.
В RM подробнейшим образом описан механизм транзакций чтения и записи.
ST там, конечно, наворотили, поэтому работа с битами управления для байтов N (последнего), N-1, N-2-... - отличается.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

У вас много ошибок и неточностей в коде. Основные:

1) Мастер после подачи старт-условия должен дождаться появления флага SB, вдруг шина занята...

2) Мастер после передачи адреса слейва+0, должен дождаться появления флага ADDR=1.

3) Чтобы передать байт от мастера к слейву, надо дождаться флага TxE=1, и только потом передавать байт.

4) Чтобы принять байт от слейва, надо дождаться флага RxNE=1, и только после этого принять байт.

5) После каждого ивента надо чистить флаги.

Учтите слейв передаёт столько байт, сколько требует мастер, не больше, не меньше.

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

On 2/9/2021 at 11:50 PM, =GM= said:

У вас много ошибок и неточностей в коде. Основные:

1) Мастер после подачи старт-условия должен дождаться появления флага SB, вдруг шина занята...

2) Мастер после передачи адреса слейва+0, должен дождаться появления флага ADDR=1.

3) Чтобы передать байт от мастера к слейву, надо дождаться флага TxE=1, и только потом передавать байт.

4) Чтобы принять байт от слейва, надо дождаться флага RxNE=1, и только после этого принять байт.

5) После каждого ивента надо чистить флаги.

Учтите слейв передаёт столько байт, сколько требует мастер, не больше, не меньше.

 

1) А это разве не ожидание?

while ((I2C1->SR1 & I2C_SR1_SB) == 0) ;

2) А это не проверка флага ADDR?

while (true)
	{
		uint16_t SR1 = I2C1->SR1 ;
		
		if (SR1 & I2C_SR1_AF)
		{
			I2C1->SR1 &= ~I2C_SR1_AF ;
			
			stop_I2C () ;
			return false ;
		}
		
		if (SR1 & I2C_SR1_ADDR)
		{
			(void) I2C1->SR2 ;
			
			break ;
		}
		
		

3) Либо через флаг BTF.

if (SR1 & I2C_SR1_BTF)
		{
			break ;
		}
	

4) А разве я делаю по другому?

while ((I2C1->SR1 & I2C_SR1_RXNE) == 0) ;
	
	return I2C1->DR ;

 

Вообще я уже нашел как обойти глюк с лишним байтом.

Запись флага STOP в CR1 нужно делать ДО окончания приема последнего байта, а не после.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

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

×
×
  • Создать...