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

stm32l0 отдельные старт, передача данных и стоп в i2c

4 часа назад, Mark16 сказал:

ну мне без автостопа как-то надо. 

Реально надо или вы себе это придумали и героически боретесь?

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


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

21 час назад, Obam сказал:

Каких (что значит) пакетов?

перепутал, написал пакетов, на самом деле байт

21 час назад, Obam сказал:

за один заход в состоянии получать?

да

18 часов назад, VladislavS сказал:

Реально надо или вы себе это придумали и героически боретесь?

Пытаюсь этот исходник на stm32 перенести .
И там такая функция есть

Спойлер
void SSD1306Device::ssd1306_fillscreen(uint8_t fill) {
	ssd1306_setpos(0, 0);
	ssd1306_send_data_start();	// Initiate transmission of data
	for (uint16_t i = 0; i < 128 * 8 / 4; i++) {
		ssd1306_send_byte(fill);
		ssd1306_send_byte(fill);
		ssd1306_send_byte(fill);
		ssd1306_send_byte(fill);
	}
	ssd1306_send_data_stop();	// Finish transmission
}

 

 

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


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

после передачи нескольких байт затыкается
И что, NACK от ведомого? При заполнении видеопамяти?

Смотреть что внутри ssd1306_send_byte(fill).

Ещё: занятная SSD1306 м/с (глянул datasheet).
void SSD1306Device::ssd1306_fillscreen(uint8_t fill) - мутная по смыслу, т.к. не заполнит дисплей при страничной адресации (а только при вертикальной или горизонтальной адресации)
128 * 8 / 4 - зачем, и по 4 байта?
Когда надо либо 8 строк по 128 байт (страничной адресацией), либо одним чохом 1024 байта (вертикальной\горизонтальной адресацией).
Изменено пользователем Obam

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


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

Я подключал этот дисплей на шину SPI и отправлял данные блоками по 128 байт (дополнительно обрамляя их командами записи в определённые ячейки памяти, ибо похожий контроллер SH1106 не умел переходить на следующую строку - приходилось самому. А SSD1306 умеет, но команда ему не мешает - так что оставил как есть).

И, да. Присоединяюсь к рекомендации Obam - слать блоками по 128 байт.
Вообще, перепишите функцию отправки. У вас исходник под AVR, а тут всё же более продвинутая периферия - можно много чего сделать удобнее.

VladislavS привёл в пример готовый код. Если хотите максимально просто - приложу свой. Не использует плюсы, всё на регистрах. Заточено под F401 (но I2C периферия в L05x не особенно отличается - проверил по референсу).

 

У меня НЕ обрабатываются пограничные случаи, как отправка OVER 255 байт в устройство (т.к. столько не требовалось, да и микросхема EEPROM, под которую писал код, всего на 256 байт. Функции в случае ошибки при передаче возвращают -1 (0xFF), в случае успеха - 0.

Спойлер

Сигнатуры функций:
DevAddr - Адрес устройства на шине. 7 бит - 0x00~0x7F
MemAddr - Адрес в устройстве (идёт вслед за DevAddr - так микросхемы памяти получают начальный адрес)
*Mem - Указатель на массив байт, откуда (или куда) будет читаться (писаться) в (из) ведомого
Len - Количество байт в массиве. НЕ суммарное количество байт. Не учитывается адрес, заголовок и стопы-старты.

Чтение одного байта из [DevAddr] по адресу [MemAddr]. Результат будет положен по адресу указателя [*Mem]

uint8_t I2C_RdByte(uint8_t DevAddr, uint8_t MemAddr, uint8_t *Mem) {
	I2C1->CR1 |= I2C_CR1_PE;
	I2C1->CR1 |= I2C_CR1_START;
	
	while (!(I2C1->SR1 & I2C_SR1_SB)){};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	// send device address
	I2C1->DR = (DevAddr << 1);
	while (!(I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR))){};
	if (I2C1->SR1 & (I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR)) {
		I2C1->SR1 = 0;
		I2C1->CR1 |= I2C_CR1_STOP;
		return -1;
	};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	I2C1->DR = MemAddr;
	while (!(I2C1->SR1 & I2C_SR1_BTF)) {__NOP();};
	I2C1->CR1 &= ~I2C_CR1_ACK;
	I2C1->CR1 |= I2C_CR1_START;
	while (!(I2C1->SR1 & I2C_SR1_SB)){};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	// send device address + RD
	I2C1->DR = (DevAddr << 1) | 1;
	while (!(I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR))){};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	if (I2C1->SR1 & I2C_SR1_RXNE) {
		*Mem = I2C1->DR;
	};
	I2C1->CR1 |= I2C_CR1_STOP;
	return 0;
}

Чтение массива из [DevAddr], начиная с адреса в его памяти [MemAddr], количеством [Len]. Будет помещено в массив по указателю [*Mem]

uint8_t I2C_RdArray(uint8_t DevAddr, uint8_t MemAddr, uint8_t *Mem, uint8_t Len) {
	uint8_t i = 0;
	
	
	I2C1->CR1 |= I2C_CR1_PE;
	I2C1->CR1 |= I2C_CR1_START;
	
	while (!(I2C1->SR1 & I2C_SR1_SB)){};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	// send device address
	I2C1->DR = (DevAddr << 1);
	while (!(I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR))){};
	if (I2C1->SR1 & (I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR)) {
		I2C1->SR1 = 0;
		I2C1->CR1 |= I2C_CR1_STOP;
		return -1;
	};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	I2C1->DR = MemAddr;
	while (!(I2C1->SR1 & I2C_SR1_BTF)) {__NOP();};
	I2C1->CR1 |= I2C_CR1_START;
	while (!(I2C1->SR1 & I2C_SR1_SB)){};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	I2C1->CR1 |= I2C_CR1_ACK;		// Going to read some data.
	// send device address + RD
	I2C1->DR = (DevAddr << 1) | 1;
	while (!(I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR))){};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	while (i < Len) {
		if (I2C1->SR1 & I2C_SR1_RXNE) {
			Mem[i] = I2C1->DR;
			i++;
			if (i == (Len - 1)) {
				I2C1->CR1 &= ~I2C_CR1_ACK;
			};
		};
	};
	I2C1->CR1 |= I2C_CR1_STOP;
	return 0;
}

Запись одного байта в  [DevAddr] по адресу [MemAddr]. Байт читается по указателю [*Mem]

uint8_t I2C_WrByte(uint8_t DevAddr, uint8_t MemAddr, uint8_t *Mem) {
	I2C1->CR1 |= I2C_CR1_PE;
	I2C1->CR1 |= I2C_CR1_START;
	
	while (!(I2C1->SR1 & I2C_SR1_SB)){};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	// send device address
	I2C1->DR = (DevAddr << 1);
	while (!(I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR))){};
	if (I2C1->SR1 & (I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR)) {
		I2C1->SR1 = 0;
		I2C1->CR1 |= I2C_CR1_STOP;
		return -1;
	};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	// Send Memory position to write start.
	I2C1->DR = MemAddr;
	while (!(I2C1->SR1 & I2C_SR1_BTF)) {__NOP();};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	I2C1->DR = *Mem;
	while (!(I2C1->SR1 & I2C_SR1_BTF)) {__NOP();};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	I2C1->CR1 |= I2C_CR1_STOP;
	
	uint8_t i;
	while (I2C_RdByte(DevAddr, 0, &i)) {delay_ms(1);};
	
	return 0;
}

Запись массива в [DevAddr], начиная с адреса в его памяти [MemAddr], количеством [Len]. Указатель на память - [*Mem]

uint8_t I2C_WrArray(uint8_t DevAddr, uint8_t MemAddr, uint8_t *Mem, uint8_t Len) {
	uint8_t i = 0;
	I2C1->CR1 |= I2C_CR1_PE;
	I2C1->CR1 |= I2C_CR1_START;
	
	while (!(I2C1->SR1 & I2C_SR1_SB)){};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	// send device address
	I2C1->DR = (DevAddr << 1);
	while (!(I2C1->SR1 & (I2C_SR1_ADDR | I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR))){};
	if (I2C1->SR1 & (I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR)) {
		I2C1->SR1 = 0;
		I2C1->CR1 |= I2C_CR1_STOP;
		return -1;
	};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	I2C1->DR = MemAddr;
	while (!(I2C1->SR1 & I2C_SR1_BTF)) {__NOP();};
	(void)I2C1->SR1;
	(void)I2C1->SR2;
	while (i < (Len)) {
		(void)I2C1->SR1;
		(void)I2C1->SR2;
		I2C1->DR = Mem[i];
		i++;
		while (!(I2C1->SR1 & I2C_SR1_TXE)) {__NOP();};
	};
	I2C1->CR1 |= I2C_CR1_STOP;
	
	while (I2C_RdByte(DevAddr, 0, &i)) {delay_ms(1);};
	
	return 0;
}

 

 

 

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


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

7 часов назад, Mark16 сказал:

Пытаюсь этот исходник на stm32 перенести .

Не вижу тут требование отсутствия Autoend. Количество данных заранее известно. В моей функции просто заменяете отправку из буфера base()->TXDR = *data++; на отправку сначала одной константы I2C1->TXDR = SSD1306_DATA;, а потом в цикле I2C1->TXDR = fill;.

И вообще, подобные вещи с DMA делают. Но это можно следующим шагом переделать после того как просто заработает. Ну и осциллоглядом контролировать обязательно.

 

23 минуты назад, AlanDrakes сказал:

Заточено под F401 (но I2C периферия в L05x не особенно отличается - проверил по референсу).

При всём уважении, но у F401 реализация I2C старая как у F103 скорее. У L051 новая как в моих функциях.

 

Спойлер

image.thumb.png.7a32a16f37f28e7335d06397adc081e4.png

 

 

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


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

19 минут назад, VladislavS сказал:

При всём уважении, но у F401 реализация I2C старая как у F103 скорее. У L051 новая как в моих функциях.

Вы правы, я что-то не те функции взял. И, да. У 401 как раз старый блок I2C. Но фукнкции таки работают.
Исправлюсь.

Тут проект был меньше и в основном читает данные с датчиков. Собирается как раз под L051.

I2C_ReadArray - читает как есть - это работает с AHT10. ИМХО - забавный датчик. Отправляем команду через запись трёх байт, ждём, читаем тупо массивом.

I2C_ReadArray_from8 - аналогично EEPROM'у. Уже нормально работает с BMP(BME)280. Вычитывает массив, начиная с адреса [Addr] и складывает в память.

Спойлер
uint8_t I2C_ReadArray(uint8_t DevAddr, uint8_t *Array, uint8_t DataToRead) {
	uint8_t Indx=0;
	(void) (I2C1->ISR);
	I2C1->ICR = I2C_ICR_STOPCF;
	I2C1->ICR = 0x3F38;
	I2C1->CR2 = (I2C_CR2_AUTOEND | DevAddr << 1 | I2C_CR2_RD_WRN | (DataToRead<<16) | I2C_CR2_START);
	while (Indx < DataToRead) {
		while (!(I2C1->ISR & I2C_ISR_RXNE)) {};
		Array[Indx] = (I2C1->RXDR);
		Indx++;
	};
	return Indx;
}

uint8_t I2C_ReadArray_from8(uint8_t DevAddr, uint8_t Addr, uint8_t *Array, uint8_t DataToRead) {
	uint8_t Indx=0;
	uint8_t result = 0;
	I2C1->TXDR = Addr;
	I2C1->ICR = 0x3F38;
	I2C1->CR2 = (((DevAddr << 1) & 0xFE) | (1<<16) | I2C_CR2_START);
	while (!(I2C1->ISR & (I2C_ISR_TXE | I2C_ISR_NACKF))) {};
	if (I2C1->ISR & I2C_ISR_NACKF) {
		result = 1;
	} else {
		I2C1->CR2 = (((DevAddr << 1) & 0xFE) | I2C_CR2_RD_WRN | (DataToRead<<16) | I2C_CR2_START);
		while (Indx < DataToRead) {
			while (!(I2C1->ISR & I2C_ISR_RXNE)) {};
			Array[Indx] = (I2C1->RXDR);
			Indx++;
		}
	}
	I2C1->CR2 = I2C_CR2_STOP;
	return result;
}

// Запись "Сырого" массива. Если нужно отправить адрес - то перед циклом While (i < DataToWrite) отправить байты адреса.
uint8_t I2C_WriteArray(uint8_t DevAddr, uint8_t *Array, uint8_t DataToWrite) {
	uint8_t i;
	I2C1->ICR = 0x3F38;
	I2C1->TXDR = *Array;
	Array++;
	I2C1->CR2 = (DevAddr << 1 | ((DataToWrite)<<16) | I2C_CR2_START);
	if (DataToWrite > 1) {
		i = 1;
		while (i < DataToWrite) {
			while (!(I2C1->ISR & (I2C_ISR_TXE | I2C_ISR_NACKF))) {};
			if (I2C1->ISR & I2C_ISR_NACKF) {
				I2C1->ICR = I2C_ICR_STOPCF | I2C_ICR_NACKCF;
				delay_ms(1);
				return -1;
			} else {
				I2C1->TXDR = *Array;
				Array++;
				i++;
			};
		};
	};
	while (!(I2C1->ISR & (I2C_ISR_TXE | I2C_ISR_NACKF))) {};
	I2C1->CR2 = I2C_CR2_STOP;
	return 0;
}

 

ЗЫ: Вообще не люблю шину I2C. Кроме экономии проводов даёт только проблемы.
То после подачи питания контроллер слишком быстро начинает работу с шиной (а некоторые хитро вы.... сделанные микросхемы ещё не очухались), то какой-то чип воспринял переходный процесс как START, то ещё что... Уууууу. То подтяжки слабые, то подтяжки сильные... И гнать шину нельзя.

Хотя пара MAX17043 + M24C02 на шине пока что уживаются хорошо.

Изменено пользователем AlanDrakes

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


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

Действительно, вывод на дисплей лучше через ДМА. Но это хорошо работает когда есть кадровый буфер, а под него памяти надо много. Ну и да, i2c - не лучший видеоинтерфейс.

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


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

...работает когда есть кадровый буфер, а под него памяти надо много
В рамках данной темы целый килобайт (((-8Ж - дисплей 128*64 точки (8 строчек по 8 бит).

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


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

Ну при таком раскладе я бы не раздумывал.

Посмотрел, оказывается я с ним когда-то работал, правда через SPI

void SSD1306_UpdateScreen(void) {
    _CS_0();
	for (uint8_t m = 0; m < 8; m++) {
		SSD1306_WRITECOMMAND(0xB0 + m);
		SSD1306_WRITECOMMAND(0x00);
		SSD1306_WRITECOMMAND(0x10);
        _DAT();
        HAL_SPI_Transmit(&hspi1, &SSD1306_Buffer[SSD1306_WIDTH * m], SSD1306_WIDTH, 100);
	}
    _CS_1();
}

И что самого удивляет, без ДМА и даже без прерываний. Так делать не надо...

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


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

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

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

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

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

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

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

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

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

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