I2C. Не передаются данные из TXDR.

Кто любит RISC в жизни, заходим, не стесняемся.
Ответить
DDenisov
Родился
Сообщения: 13
Зарегистрирован: Вс июл 03, 2016 17:16:23

I2C. Не передаются данные из TXDR.

Сообщение DDenisov »

Пытаюсь получить данные по I2C с установленного на плату акселерометра. Отладочная плата STM32 F3 Discovery. Акселерометр LSM303DLHC подключен (по схеме к плате): SCL на PB6, SDA на PB7. Адрес акселерометра по даташиту 0011001, т.е. 0x19, c битом записи будет 0x33. Адрес одного из регистров 0х28 (это OUT_X_L_A с младшими битами ускорения по оси X).

План был таков:
1) Настроить пины. Контроллер STM32F303VCT6, пины PB6 и PB7 принадлежат порту GPIOB. Тактирование порта осуществляется шиной AHB. Пины настраиваются без pull-up/pull-down, т.к. на схеме отладочной платы они оснащены резисторами. Также пины настраиваются на выполнение альтернативной функции AF4 (согласно даташиту STM32F303VCT6 эта функция представлят собой SDA и SCL линии I2C1).
2) Настройка I2C1. Содержимое регистра TIMINGR добыл из CubeMX, включив в нем I2C1 на PB6 и PB7 (параметры I2C - на вкладке Configuraion). Rise time = 500 нс и Fall time = 100 нс взяты из даташита на LSM303DLHC (там даны диапазоны, и я брал посерединке 500 и 100). CubeMX сгенерировал содержимое TIMINGR. В конце функции initSystem() включается периферия I2C1.
3) Функция main просто настраивает СR2 для I2C1 (для записи, чтобы ) и затем устанавливает START. Далее посылает адрес регистра 0x28 и потом она должна читать данные.

Программа компилируется, но не работает дальше строки 40. Цикл не прерывается, из-за того, что (I2C1->ISR & I2C_ISR_TXIS) == 0 постоянно выполняется. При этом, данные 0x28 находятся в регистре TXDR (вижу, запуская в отладчике uVision). После установки START, бит BUSY в I2C1->ISR устанавливается (тоже в отладчике смотрю).

Прошу подсказать, что не так. Сейчас копаю в сторону того, что пины PB6 и PB7 настроены некорректно. Пока безрезультатно. Код ниже.

Акселерометр рабочий, т.к. демонстрационные приложения к плате работают.

Код: Выделить всё

#include "stm32f3xx.h"                  // Device header

void initSystem()
{
	// LSM303DLHC  SCL на PB6, SDA на PB7.
	RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
	GPIOB->MODER |= GPIO_MODER_MODER6_1|GPIO_MODER_MODER7_1;
	GPIOB->OTYPER |= GPIO_OTYPER_OT_6|GPIO_OTYPER_OT_7;
	GPIOB->PUPDR &= ~(GPIO_PUPDR_PUPDR6 | GPIO_PUPDR_PUPDR7 );
	GPIOB->OSPEEDR |= GPIO_OSPEEDER_OSPEEDR6 |GPIO_OSPEEDER_OSPEEDR7;
	// Альт. функция AF4 (0100=0x4) вкл. I2C1_SCL на PB6 и I2C1_SDA на PB7.
	GPIOB->AFR[0] |= (0x4 << GPIO_AFRL_AFRL6_Pos);
	GPIOB->AFR[0] |= (0x4 << GPIO_AFRL_AFRL7_Pos);
	
	// Вкл. I2C peripheral clock.
	RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
	// Уст. TIMINGR (CubeMX).
	I2C1->TIMINGR = 0x00501E26;
	// Peripheral Enable.
  I2C1->CR1 |= I2C_CR1_PE;
}

// Переменная для данных.
uint32_t buffer[5];
// Счетчик.
uint32_t i = 5;

int main()
{
	initSystem();
	
	// Уст. CR2. Запись.
	I2C1->CR2 |= (I2C_CR2_NBYTES & 1); 		// 1 байт (NBYTES).
	I2C1->CR2 &= ~I2C_CR2_RD_WRN;			// Запись. Бит 0 в RD_WRN.
	I2C1->CR2 |= 0x19 << 1; 					// Адрес акселерометра (без r/w бита).
	I2C1->CR2 |= I2C_CR2_START;				// Уст. START. Мастер посылает СТАРТ, адрес слейва и R/W бит.

	while ((I2C1->ISR & I2C_ISR_TC) == 0);		// Ждем Transfer Complete.
	I2C1->TXDR = 0x28;						// Новые данные (адрес внутреннего регистра) кладем в TXDR.
	while ((I2C1->ISR & I2C_ISR_TXIS) == 0);		// Ждем пока TXDR станет пустым и готовым к новым данным. ------ ЗДЕСЬ программа виснет-----
	
	// Уст. CR2 для чтения.
	I2C1->CR2 = I2C_CR2_AUTOEND | (I2C_CR2_NBYTES & 5) | I2C_CR2_RD_WRN | 0x19 << 1;
	I2C1->CR2 |= I2C_CR2_START;
	// Чтение.
	while (i-- > 0) 
	{
		while((I2C1->ISR & I2C_ISR_RXNE) == 0);				// Ждем пока recieve data register будет готов.
		buffer [i] = I2C1->RXDR;							// Копируем данные из recieve data register.
	}

	while(1)
	{
		
	}
}
Вложения
main.c
(2.05 КБ) 143 скачивания
Реклама
holy_0dmin
Открыл глаза
Сообщения: 43
Зарегистрирован: Чт май 24, 2012 09:19:34
Откуда: Ярославль
Контактная информация:

Re: I2C. Не передаются данные из TXDR.

Сообщение holy_0dmin »

I2C у STM32 больная тема, что на F, что на L серии. В такую проблему тоже вляпывался на STM32F030F4P6. Не могу вспомнить в чем именно причина была, но мне удалось завести простенький акселерометр MMA7660 только при помощи SPL и написания конечного автомата Могу выложить свой файл с этим конечным автоматом, там акселерометр настраивается и потом с него постоянно снимаются данные по ускорению плюс проверка на подвисание I2C (косяк описан во всех ERRATA и его не собираются чинить).
Реклама
Аватара пользователя
dosikus
Друг Кота
Сообщения: 3604
Зарегистрирован: Пн июл 28, 2008 22:12:01

Re: I2C. Не передаются данные из TXDR.

Сообщение dosikus »

holy_0dmin писал(а):I2C у STM32 больная тема, что на F, что на L серии. В такую проблему тоже вляпывался на STM32F030F4P6.
Не может быть. На F1 да I2C убог, на новой периферии все работает шикарно. И SPL там даром не нужен надо уметь читать.

Осталось только таймауты внедрить. :)))
Спойлер

Код: Выделить всё


#define RDA5807_OWN_ADDRESS (0x11)
#define EEPROM_OWN_ADDRESS (0x50)
uint8_t  temp1, temp2;





	void rda5807_init(void)
	{
		RCC->AHBENR |= RCC_AHBENR_GPIOFEN;
		
		RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
		RCC->CFGR3 |= RCC_CFGR3_I2C1SW; 
		
		GPIOF->AFR[0] |= (1<<(4*0)) |(1<<(4*1));
		GPIOF->MODER &= ~(GPIO_MODER_MODER0 |GPIO_MODER_MODER1);
		GPIOF->MODER |= (GPIO_MODER_MODER0_1 |GPIO_MODER_MODER1_1);
		GPIOF->OTYPER |=(GPIO_OTYPER_OT_0 |GPIO_OTYPER_OT_1);
		
		I2C1->TIMINGR = (uint32_t)0x00B01A4B; 
		I2C1->CR1 = I2C_CR1_PE; 
  
	
		
	}

	
	void 	rda5807_send_reg(uint8_t reg, uint8_t data_h,uint8_t data_l)
		{
		I2C1->CR2 =  I2C_CR2_AUTOEND | (3<<16) | (RDA5807_OWN_ADDRESS<<1); 
		
			/* Check Tx empty */
	  while (!(I2C1->ISR & I2C_ISR_TXE) );
	
	 I2C1->TXDR = reg; /* Byte to send */
	 I2C1->CR2 |= I2C_CR2_START; /* Go */
		
		while (!(I2C1->ISR & I2C_ISR_TXIS) );
		I2C1->TXDR = data_h; /* Byte to send */
		while (!(I2C1->ISR & I2C_ISR_TXIS) );
		
		I2C1->TXDR = data_l; /* Byte to send */
	
			
		}
uint16_t 	rda5807_read16_reg(uint8_t reg)
{ 
	 uint16_t temp=0;
	I2C1->CR2 =   (1<<16) | (RDA5807_OWN_ADDRESS<<1); 
	 while (!(I2C1->ISR & I2C_ISR_TXE) );
	 I2C1->TXDR = reg; /* Byte to send */
	 I2C1->CR2 |= I2C_CR2_START; /* Go */
	 while (!(I2C1->ISR & I2C_ISR_TC) ){};
	 I2C1->CR2 =  I2C_CR2_AUTOEND | (2<<16) | 
		            (RDA5807_OWN_ADDRESS<<1) |
		             I2C_CR2_RD_WRN |
		             I2C_CR2_NACK; 
	 I2C1->CR2 |= I2C_CR2_START; /* Go */
	 while (!(I2C1->ISR & I2C_ISR_RXNE) ){};
	 temp  = (uint16_t)(I2C1->RXDR <<8);
		while (!(I2C1->ISR & I2C_ISR_RXNE) ){};
	 temp |=  (uint16_t)I2C1->RXDR; 
		 return temp;
}
	

void 	rda5807_write16_reg(uint8_t reg, uint16_t data )
{
	I2C1->CR2 =  I2C_CR2_AUTOEND | (3<<16) | (RDA5807_OWN_ADDRESS<<1); 
		
			/* Check Tx empty */
	  while (!(I2C1->ISR & I2C_ISR_TXE) );
	
	 I2C1->TXDR = reg; /* Byte to send */
	 I2C1->CR2 |= I2C_CR2_START; /* Go */
		
		while (!(I2C1->ISR & I2C_ISR_TXIS) );
		I2C1->TXDR = (uint8_t)(data>>8); /* Byte to send */
		while (!(I2C1->ISR & I2C_ISR_TXIS) );
		
		I2C1->TXDR = (uint8_t)(data &0x00FF); /* Byte to send */
	
}


uint8_t eeprom_read(uint16_t address)
{
	 uint8_t temp=0;
	I2C1->CR2 =   (2<<16) | (EEPROM_OWN_ADDRESS<<1); 
	 while (!(I2C1->ISR & I2C_ISR_TXE) );
	 I2C1->TXDR = (uint8_t) (address>>8); /* Byte to send */
	 I2C1->CR2 |= I2C_CR2_START; /* Go */
		while (!(I2C1->ISR & I2C_ISR_TXIS) );
		I2C1->TXDR = (uint8_t)(address &0x00FF); /* Byte to send */
	 while (!(I2C1->ISR & I2C_ISR_TC) ){};
	 I2C1->CR2 =  I2C_CR2_AUTOEND | 
		             (1<<16) | 
		            (EEPROM_OWN_ADDRESS<<1) |
		             I2C_CR2_RD_WRN |
		             I2C_CR2_NACK; 
	 I2C1->CR2 |= I2C_CR2_START; /* Go */
	 while (!(I2C1->ISR & I2C_ISR_RXNE) ){};
	 temp  = I2C1->RXDR ;
	
		 return temp;
}

void eeprom_write(uint16_t address, uint8_t data)
{
	
	I2C1->CR2 =  I2C_CR2_AUTOEND | (3<<16) | (EEPROM_OWN_ADDRESS<<1); 
		
			/* Check Tx empty */
	  while (!(I2C1->ISR & I2C_ISR_TXE) );
	
	 I2C1->TXDR =(uint8_t) (address>>8); /* Byte to send */
	 I2C1->CR2 |= I2C_CR2_START; /* Go */
		
		while (!(I2C1->ISR & I2C_ISR_TXIS) );
		I2C1->TXDR = (uint8_t)(address &0x00FF); /* Byte to send */
	
		while (!(I2C1->ISR & I2C_ISR_TXIS) );
		
		I2C1->TXDR = data ; /* Byte to send */
	 	
}

void eeprom_pagewrite(uint16_t address, uint8_t *buf,uint8_t nbytes)
{
	uint8_t index=0;
	I2C1->CR2 =  I2C_CR2_AUTOEND | ((2+nbytes)<<16) | (EEPROM_OWN_ADDRESS<<1); 
		
			/* Check Tx empty */
	  while (!(I2C1->ISR & I2C_ISR_TXE) );
	
	 I2C1->TXDR =(uint8_t) (address>>8); /* Byte to send */
	 I2C1->CR2 |= I2C_CR2_START; /* Go */
		
		while (!(I2C1->ISR & I2C_ISR_TXIS) );
		I2C1->TXDR = (uint8_t)(address &0x00FF); /* Byte to send */
	for(index=0;index<=nbytes-1;index++)
	 {
		while (!(I2C1->ISR & I2C_ISR_TXIS) );
		
		I2C1->TXDR = buf[index] ; /* Byte to send */
	 }
}

void eeprom_pageread(uint16_t address,uint8_t* buf,uint8_t nbytes)
{
	
	uint8_t index=0;
	I2C1->CR2 =   (2<<16) | (EEPROM_OWN_ADDRESS<<1); 
	 while (!(I2C1->ISR & I2C_ISR_TXE) );
	 I2C1->TXDR = (uint8_t) (address>>8); /* Byte to send */
	 I2C1->CR2 |= I2C_CR2_START; /* Go */
		while (!(I2C1->ISR & I2C_ISR_TXIS) );
		I2C1->TXDR = (uint8_t)(address &0x00FF); /* Byte to send */
	 while (!(I2C1->ISR & I2C_ISR_TC) ){};
	 I2C1->CR2 =  I2C_CR2_AUTOEND |
            		 (nbytes<<16) | 
		            (EEPROM_OWN_ADDRESS<<1) |
		             I2C_CR2_RD_WRN |
		             I2C_CR2_NACK; 
	 I2C1->CR2 |= I2C_CR2_START; /* Go */
		for(index=0;index<=nbytes-1;index++)
	 { 
	 while (!(I2C1->ISR & I2C_ISR_RXNE) ){};
	 buf[index]  = I2C1->RXDR ;
	
	 }
}

Последний раз редактировалось dosikus Ср июл 06, 2016 12:42:47, всего редактировалось 1 раз.
holy_0dmin
Открыл глаза
Сообщения: 43
Зарегистрирован: Чт май 24, 2012 09:19:34
Откуда: Ярославль
Контактная информация:

Re: I2C. Не передаются данные из TXDR.

Сообщение holy_0dmin »

L1 тоже кривоват. Гораздо лучше, чем F, но подвисать может.
А SPL улучшает читаемость кода, хотя без него тоже можно, I2C не самая сложная периферия.
Реклама
Эиком - электронные компоненты и радиодетали
Аватара пользователя
dosikus
Друг Кота
Сообщения: 3604
Зарегистрирован: Пн июл 28, 2008 22:12:01

Re: I2C. Не передаются данные из TXDR.

Сообщение dosikus »

holy_0dmin писал(а): А SPL улучшает читаемость кода,
Не зема, это фикция, наивная надежда для хомячков...
Реклама
DDenisov
Родился
Сообщения: 13
Зарегистрирован: Вс июл 03, 2016 17:16:23

Re: I2C. Не передаются данные из TXDR.

Сообщение DDenisov »

В общем, пока ничего не работает :) Я научился пользоваться отладчиком. Теперь, смотрю на содержимое регистров и т.п. очень занятно.

Вопрос вот в чем. Чтение из регистров акселерометра не работает (в RXDR ничего не попадает). Интересно то, что плата как-то входит в состояние, когда на PB7(SDA) мультиметр постоянно показывает LOW и любая дальнейшая отладка приводит к выставленному Arbitration Lost в ISR. Т.е. я пару раз запускаю программу и она дорабатывает до чтения. Но на следующем запуске появляется Arbitration Lost. Причем сразу, в начале работы I2C (после START), и потом уж не исчезает даже после ресета платы.

Может ли такое быть, что микросхема акселерометра так делает после неудачного чтения? Согласно даташиту в акселерометр встроена какая-то система инкремента адреса чтения. Может она мешает прочитать один регистр?

Код: Выделить всё

#include "stm32f3xx.h"                  // Device header

void initSystem()
{
	// LSM303DLHC wiring: SCL to PB6, SDA to PB7.
	// Enable port B.
	RCC->AHBENR |= RCC_AHBENR_GPIOBEN;
	// Enable I2C peripheral clock.
	RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;

	
	// Set pin PB6 and PB7 to  Alternate Function Mode.
	// Select AF4 (0100=0x4) to toggle I2C_SCL function on pin PB6 and I2C_SDA on pin PB7.
	GPIOB->AFR[0] |= (0x4 << GPIO_AFRL_AFRL6_Pos);
	GPIOB->AFR[0] |= (0x4 << GPIO_AFRL_AFRL7_Pos);
	GPIOB->MODER |= (GPIO_MODER_MODER6_1|GPIO_MODER_MODER7_1);
	// Set pin PB6 and PB7 to Open-drain ouput type.
	GPIOB->OTYPER |= GPIO_OTYPER_OT_6|GPIO_OTYPER_OT_7;
	// Pull-up.
	GPIOB->PUPDR |= (0x1 << GPIO_PUPDR_PUPDR6_Pos);
	GPIOB->PUPDR |= (0x1 << GPIO_PUPDR_PUPDR7_Pos);
	// High output speed.
	GPIOB->OSPEEDR |= (0x0 << GPIO_OSPEEDER_OSPEEDR6_Pos) | (0x0 << GPIO_OSPEEDER_OSPEEDR7_Pos);
	

	// Set up TIMINGR (CubeMX).
	I2C1->TIMINGR = (uint32_t)0x00101D2D;
	// Peripheral Enable.
  I2C1->CR1 |= I2C_CR1_PE;
}

// Variable to store the data.
uint32_t buffer[5];
// Counter.
uint32_t i = 5;

int main()
{
	initSystem();
	
	// Set up CR2 register for data writing.
	I2C1->CR2 |= (0x2 << I2C_CR2_NBYTES_Pos); 		// 2 bytes to transfer (NBYTES).
	I2C1->CR2 |= (0x1 << I2C_CR2_AUTOEND_Pos);		// AUTOEND=1.
	I2C1->CR2 &= ~I2C_CR2_RD_WRN;					// Write. Set 0 in RD_WRN bit for transfer direction in master mode.
	I2C1->CR2 |= (0x19 << 1); 						// Accelerometer address (without r/w bit).
	I2C1->TXDR = 0x28;										// Put data (inernal registry address) into TXDR.
	I2C1->CR2 |= I2C_CR2_START;						// Set START bit. Master sends start condition, slave address and R/W bit.
	while ((I2C1->ISR & I2C_ISR_TXIS) == 0);						// Wait for TXDR is empty and ready for the new data.

	
	// Set up CR2 register for data reading and set START bit.
	I2C1->CR2 = (I2C_CR2_NBYTES & 5) | I2C_CR2_RD_WRN | 0x19 << 1;
	I2C1->CR2 |= I2C_CR2_START;
	// Read the data.
	while (i-- > 0) 
	{
		buffer [i] = I2C1->RXDR;													// Copy the data from recieve data register.
		while((I2C1->ISR & I2C_ISR_RXNE) == 0);						// Wait for recieve data register is ready.
	}

	while(1)
	{
		
	}
}
Реклама
holy_0dmin
Открыл глаза
Сообщения: 43
Зарегистрирован: Чт май 24, 2012 09:19:34
Откуда: Ярославль
Контактная информация:

Re: I2C. Не передаются данные из TXDR.

Сообщение holy_0dmin »

Для начала не надо держать включенным окно отладчика I2C во время чтения по I2C - он читает данные из регистров ещё до программы и это в корне меняет логику работы.
Ответить

Вернуться в «ARM»