Jump to content
G1KuL1N

Программный I2C для STM32

Recommended Posts

В общем понадобилось тут подвесить FRAM к STM32, а пины аппаратных I2C были уже заняты, ну решил не менять пины, а добавить себе на вооружение на всякий, программный I2C. То что удалось нагуглить либо работало криво, либо приходилось допиливать и допиливать. Ну и написал свой с блекджеком и ш.. Так как в сети примеров мало программного I2C на STM32 то решил поделиться. Вдруг кому пригодится. Работает с библиотекой HAL, поправить нужно только под  свой камень и свои порты и все работает.

I2C.h

#ifndef __I2C_H_H__
#define __I2C_H_H__
// G1KuL1N
// ПОДКЛЮЧИТЬ БИБЛИОТЕКУ СВОЕГО КОНТРОЛЛЕРА

#include "stm32f4xx_hal.h"

// В CUBE MX порты I2C настроить на выход (либо в main.c вручну подать тактирование на нужны GPIO) 

//---подключение шины к пинам-----------------------------------------------------
#define SCL_I HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_15);   
#define SDA_I HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_14);
#define SCL_O HAL_GPIO_WritePin(GPIOC, GPIO_PIN_15, GPIO_PIN_RESET);
#define SDA_O HAL_GPIO_WritePin(GPIOC, GPIO_PIN_14, GPIO_PIN_RESET);
//--------------------------------------------------------------------------------
void i2c_init (void);               // Инициализация шины
void i2c_start_cond (void);        // Генерация условия старт
void i2c_restart_cond (void);      // Генерация условия рестарт
void i2c_stop_cond (void) ;       // Генерация условия стоп  
uint8_t i2c_send_byte (uint8_t data) ;      //Передать байт (вх. аргумент передаваемый байт) (возвращает 0 - АСК, 1 - NACK) 
uint8_t i2c_get_byte (uint8_t last_byte) ;  //Принять байт (если последний байт то входной аргумент = 1, если будем считывать еще то 0)(возвращает принятый байт)
//--------------------------------------------------------------------------------
// ПРИМЕР ИСПОЛЬЗОВАНИЯ
//=========================================================================================
//   Запись uint16_t во внешнюю еепром (FRAM FM24CL64) или любой другой 24LC памяти, 
//	 две ячейки,  указывается адрес первой ячейки, следующая идет adr++
//=========================================================================================
//
//void FRAM_W_INT(uint16_t adr, uint16_t dat){
//i2c_start_cond ();
//i2c_send_byte (0xA2); //адрес чипа + что будем делать (записывать)
//i2c_send_byte    ((adr & 0xFF00) >> 8);  
//i2c_send_byte    (adr & 0x00FF);
//i2c_send_byte    ((dat & 0xFF00) >> 8);  
//i2c_send_byte    (dat & 0x00FF);
//i2c_stop_cond();
//}

//=========================================================================================
//   Считывание uint16_t из внешней еепром (FRAM FM24CL64) или любой другой 24LC памяти, 
//	 две ячейки,  указывается адрес первой ячейки, следующая идет adr++
//=========================================================================================
//uint16_t FRAM_R_INT(uint16_t adr){
//uint16_t dat;
//i2c_start_cond ();
//i2c_send_byte (0xA2);
//i2c_send_byte    ((adr & 0xFF00) >> 8);  
//i2c_send_byte    (adr & 0x00FF);
//i2c_restart_cond ();
//i2c_send_byte (0xA3);
//dat =  i2c_get_byte(0);	
//dat <<= 8; 
//dat |= i2c_get_byte(1);
//i2c_stop_cond();
//return dat;
//}

// G1KuL1N

#endif /* __I2C_H_H__ */

i2C.c

#include "I2C.h"

volatile uint8_t i2c_frame_error=0; 

//-----------------------------------------------------------
__STATIC_INLINE void Delay_us (uint32_t __IO us) //Функция задержки в микросекундах us
{
us *=(SystemCoreClock/1000000)/5;
	while(us--);
}

//----------------------------------------------------
void SCL_in (void) //функция отпускания SCL в 1, порт на вход (необходимо установить используемый порт) 
	{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
	}
//----------------------------------------------------
void SCL_out (void) //функция притягивания SCL в 0 (необходимо установить используемый порт) 
	{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_15;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
	SCL_O;
	}
//----------------------------------------------------
void SDA_in (void) //функция отпускания SDA в 1, порт на вход (необходимо установить используемый порт) 
	{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
	}
//----------------------------------------------------
void SDA_out (void) //функция притягивания SDA в 0 (необходимо установить используемый порт) 
	{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.Pin = GPIO_PIN_14;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
	SDA_O
	}

//----------------------------------------------------
void i2c_stop_cond (void)  // функция генерации условия стоп 
{
    uint16_t SCL, SDA;
		SCL_out(); // притянуть SCL (лог.0)
    Delay_us(10);
    SDA_out(); // притянуть SDA (лог.0)
    Delay_us(10);

    SCL_in(); // отпустить SCL (лог.1)
    Delay_us(10);
    SDA_in(); // отпустить SDA (лог.1)
    Delay_us(10);
    		
    // проверка фрейм-ошибки
    i2c_frame_error=0;		// сброс счётчика фрейм-ошибок
    SCL=SCL_I;
		SDA=SDA_I;
		if (SCL == 0) i2c_frame_error++;   // проберяем, чтобы на ноге SDA была лог.1, иначе выдаём ошибку фрейма
    if (SDA == 0) i2c_frame_error++;   // проберяем, чтобы на ноге SCL была лог.1, иначе выдаём ошибку фрейма
    Delay_us(40);
   }

void i2c_init (void) // функция инициализации шины
{
    i2c_stop_cond();   // стоп шины
    i2c_stop_cond();   // стоп шины
}
//----------------------------------------------------
void i2c_start_cond (void)  // функция генерации условия старт
{
		SDA_out(); // притянуть SDA (лог.0)
    Delay_us(10);
    SCL_out(); // притянуть SCL (лог.0)
    Delay_us(10);
}
//----------------------------------------------------
void i2c_restart_cond (void)   // функция генерации условия рестарт
{
    SDA_in(); // отпустить SDA (лог.1)
    Delay_us(10);
    SCL_in(); // отпустить SCL (лог.1)
    Delay_us(10);
    SDA_out(); // притянуть SDA (лог.0)
    Delay_us(10);
    SCL_out(); // притянуть SCL (лог.0)
    Delay_us(10);
}
//----------------------------------------------------
uint8_t i2c_send_byte (uint8_t data)  // функция  отправки байта  
{   
 uint8_t i;
 uint8_t ack=1;           //АСК, если АСК=1 – произошла ошибка
uint16_t SDA;   
	for (i=0;i<8;i++)
    {
        if (data & 0x80) 
				{
				SDA_in(); // лог.1
        }
				else 
				{
				SDA_out(); // Выставить бит на SDA (лог.0
				}
        Delay_us(10);
        SCL_in();   // Записать его импульсом на SCL       // отпустить SCL (лог.1)
        Delay_us(10);
        SCL_out(); // притянуть SCL (лог.0)
        data<<=1; // сдвигаем на 1 бит влево
					
    }
    SDA_in(); // отпустить SDA (лог.1), чтобы ведомое устройство смогло сгенерировать ACK
     Delay_us(10);
    SCL_in(); // отпустить SCL (лог.1), чтобы ведомое устройство передало ACK
     Delay_us(10);
    SDA=SDA_I;
		if (SDA==0x00) ack=1; else ack=0;    // Считать ACK

    SCL_out(); // притянуть SCL (лог.0)  // приём ACK завершён

    return ack; // вернуть ACK (0) или NACK (1)   

}
//----------------------------------------------------
uint8_t i2c_get_byte (uint8_t last_byte) // функция принятия байта
{
 uint8_t i, res=0;
	uint16_t SDA;
    SDA_in(); // отпустить SDA (лог.1)

    for (i=0;i<8;i++)
    {
        res<<=1;
        SCL_in(); // отпустить SCL (лог.1)      //Импульс на SCL
        Delay_us(10);
				SDA_in();
				SDA=SDA_I;
				if (SDA==1) res=res|0x01; // Чтение SDA в переменную  Если SDA=1 то записываем 1
        SCL_out(); // притянуть SCL (лог.0)
        Delay_us(10);
    }

    if (last_byte==0){ SDA_out();} // притянуть SDA (лог.0)     // Подтверждение, ACK, будем считывать ещё один байт
    else {SDA_in();} // отпустить SDA (лог.1)                 // Без подтверждения, NACK, это последний считанный байт
    Delay_us(10);
    SCL_in(); // отпустить SCL (лог.1)
    Delay_us(10);
    SCL_out(); // притянуть SCL (лог.0)
    Delay_us(10);
    SDA_in(); // отпустить SDA (лог.1)

    return res; // вернуть считанное значение
}

 

Share this post


Link to post
Share on other sites

редко пригождается конечно, но иногда вполне себе решение. Плюс

Share this post


Link to post
Share on other sites

Материалы вебинара «Создание беспроводных устройств на системах-на-кристалле семейства SimpleLink компании TI»

Компания Компэл, совместно с Texas Instruments провели вебинар, где инженер по применению беспроводных технологий компании TI рассказал(дублированный перевод), как на новых беспроводных системах можно реализовать несколько полезнейших в повседневной жизни функций для ваших устройств. С развитием элементной базы TI становится возможной реализация более удобных, функциональных и безопасных систем, недоступных ранее.

Подробнее...

Это решение "в лоб", но работает, просто и быстро, написано за 20 минут так как горело, и с ходу рабочего решения не нашел. Задержку на таймере конечно лучше но необязательно, для ценителей программного времени там переделать 10 минут на таймер (хотя тот кто считает такты никогда в здравом уме не  будет использовать программный  i2C, когда есть аппаратный :D). На HAL так как это встроено в большую программу которая работает с HAL. Понадобилось просто подвесить в проект FRAM но уже все было готово и переделывать было бы долго и нудно. 

Share this post


Link to post
Share on other sites

10 us - много. Практически все I2C-девайсы работают на тактовой в 100 КГц, так что можно раза в 2 поднимать частоту смело. Или задефайнить её, чтобы пользователь сам указывал.

Share this post


Link to post
Share on other sites

ESP32-DevKitC-VB позволит быстро запустить ваше первое WiFi-приложение

Отладочная плата на основе одного из самых популярных WiFi-модулей Espressif ESP32-WROVER-B позволяет в самые короткие сроки запустить приложение с поддержкой беспроводных стандартов WiFi 802.11b/g/n и Bluetooth Classic/BLE. Ресурсов встроенного в модуль чипа ESP32-D0WD хватит для решения даже очень сложных задач.

Подробнее...

обычно для софтового i2c высокой скорости и не требуется. Но я думаю что не такая уж и проблема чуть допилить предложенное решение. А на HAL оно или нет, вообще никакой разницы. Да и сколько там HALа то? Записать пин, да прочитать. Переписать это на регистры 5 минут, да и того не стоит вообще

Edited by mail_robot

Share this post


Link to post
Share on other sites
33 минуты назад, mail_robot сказал:

обычно для софтового i2c высокой скорости и не требуется

Прочтите, пожалуйста, ещё раз мой пост и попытайтесь понять, о чём я там написал.

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

интересно сколько за время передачи пакета по i2c, F4 успеет прос..ть полезной работы на блокирующих Delay. Неужели нельзя столь медленный интерфейс реализовать на таймере. выдавливать биты в прерывании таймера, самый лучший вариант это битбандинг для размотки параллельных данных в последовательность бит.

Edited by MasterElectric

Share this post


Link to post
Share on other sites

Уважаемый MasterElectric я выше уже ответил по этому поводу, не спорю с таймером лучше, но если ценим процессорное время - юзаем аппаратный I2C, и нет больше никаких "самых лучших вариантов". Во-вторых когда мне понадобился срочно программный I2C, если-б я залез сюда на форум, наткнулся бы на  Ваш пост с программным I2C с таймерами и бит-бандингом, я бы включил его себе с мыслью: вот чел молодец какой,  выложил не поленился, спасибо ему. Так что как есть, так есть.

Share this post


Link to post
Share on other sites

Разве I2C перестал поддерживать до 127 устройств на двух пинах? Или же FRAM не делит шину ни с кем?

Share this post


Link to post
Share on other sites

Пины аппартаного I2C заняты плата разведена, FRAM навесом

Share this post


Link to post
Share on other sites

у человека в первом посте все конкретно расписано что и зачем и в каких случаях. Чего накинулись то? Узкая специальная задача, применение которой - раз в сто лет заткнуть аппаратную дырку

Share this post


Link to post
Share on other sites
В 13.08.2018 в 10:55, G1KuL1N сказал:

В общем понадобилось тут подвесить FRAM к STM32

Спасибо! По твоему примеру , запустил у себя еепром 24с08 в связке с STM32F100!  

Share this post


Link to post
Share on other sites

Join the conversation

You are posting as a guest. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...

  • Similar Content

    • By Arvalon
      Здравствуйте.
      Есть камера с шиной I2C и интерфейсом "Camera Port on 21 pin ZIF connector". Информация по сигналам в наличии.
      Есть ли возможность подключить её к ПК под управлением Linux/Window (не считая физического переходника 21 пин ZIF - ножки микросхемы)? Поиск по сети выдал следующий способ. В нём используется I2C-USB переходник из USBasp программатора и микросхема MCP23017 (I2C расширитель портов). Написано что микросхема MCP23017 имеет 16 I/O портов (из 28). Далее напрашивается вопрос, хватит ли выводов микросхемы что бы подключить камеру? Читаю спецификацию на микросхему и не донца понимаю, кажется хватает.
      Опционально, можно ли избавиться от самостоятельного изготовления связки I2C-USB переходник + MCP23017 а воспользоваться готовым? Есть ли в продаже адаптер I2C-USB с достаточным количеством портов? На AliExpress'е что-то не нашлось подходящего.

      MCP23017.pdf
    • By AronIIStone
      Добрый день
      Возникла ситуация: имеется ноутбук IBM ThinkPad t20 с запароленым Bios. Пароль никто не помнит, но он зашит в 24rf08. Его можно прочитать програмкой для считывания данной микросхемы, но для этого нужен программатор. Можно ли использовать в качестве такого программатора Ардуино, чтобы он вопринимался этой программой как надо, и если можно, то как нужно подключить их и какой скетч залить в ардуинку? 
      Саму программу выложу чуть позже, если необходимо - с мобильника сижу. 
      Спасибо 
    • By User_1
      Всем доброго времени суток!
      Кто-нибудь может поделиться рабочим примером инициализации I2C в STM32F030? Ну или может увидите в моём коде ошибку
      RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE); // Включаем тактирование RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE); I2C_InitTypeDef I2C_InitStructure; GPIO_InitTypeDef GPIO_InitStructure; GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_Level_3; // Speed 50 MHz GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; // Альтернативная функция GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; // Без подтяжки GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; // Открытый сток GPIO_Init(GPIOA, &GPIO_InitStructure); I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; // Режим работы (I2C) I2C_InitStructure.I2C_OwnAddress1 = 0x11; // Адрес (не важен, если устройство - мастер) I2C_InitStructure.I2C_Timing = 0x00300619; // Значение для записи в регистр I2C_TIMINGR I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; I2C_Cmd(I2C1, ENABLE); I2C_Init(I2C1, &I2C_InitStructure); Полдня уже вожусь, никак не заводится. Компилится без ошибок, светодиод мигает (ну в основном цикле написана простая светодиодная мигалка и раз в секунду - отправка сообщения в I2C), но смотрю логическим анализатором - на ножках тишина (ну в смысле единичка всегда, они же подтянуты резисторами к плюсу)
      I2C_Timing рассчитывается калькулятором от ST (I2C Timing Configuration) на основе частоты тактирования модуля I2C,  желаемой скорости шины и ещё пары параметров, калькулятор прикрепил к сообщению
      I2C_Timing_Configuration_V1.0.1.xls
    • By Sattva
      Тема: Реверс-инжиниринг: необходимо декодировать протокол связи контроллера пром. насоса и дисплея
      Общая цель: сделать управление кондиционером, вести мониторинг его работы.
      Время на работу: около 12 часов (2 дня).
      Задание для исполнителя:
      1. Выполнить обратное декодирование протокола связи контроллера промышленного
      кондиционера и его дисплея с сенсорными кнопками.
      2. Запаять ATMega 328P или ATMega 2560. Вероятно, придется резать дорожки,
      допаять резисторы в разрыв.
      3. Совместно с заказчиком написать короткий код ввода/вывода данных для Arduino (C++).
      Могут понадобиться функции передачи данных на второй контроллер ATMega 2560 по UART.
      4. Совместно с заказчиком составить простое описание.
      5. Работа дисплея, состояние контроллера кондиционера и мониторинг на Arduino
      должны быть синхронны.
      Программировать микроконтроллер и отправлять данные на вебсервер по
      Ethernet (W5500) умею. Паять умею. Работать с микроконтроллером на уровне
      регистров процессора не умею, потому и ставлю задание.

      Протокол: неизвестен
      Шина: неизвестна
      Чип дисплея: Holtek HT1621B
      Контроллер "пульта управления": Holtek BS84B08A-3
      Фото прилагается.
      Вероятно, применяется шина SPI или I2C, что должно облегчить задачу.

      Имеется пример подобного дисплея 2009 года с выполненной
      работой. У предыдущего исполнителя ушло около 2 часов на разбор
      команд и еще столько же на написание кода, *когда поняли, как
      разбирать команды*.

      Фото с реализацией задания прилагается, но на дисплее другого типа.



      Метод выполненной работы в прошлый раз:
      1. Для получения данных с Holtek-HT46F49E по 8-ми битной шине данных использован Arduino Pro Mini 328P.
      2. Подключение: 5 дорожек перерезано и допаяно 5 резисторов по 1 кОм. Так же использовано еще 3 линии (reset и данные).
      3. Для удаленного управления использована Arduino Mega 2560 + Ethernet Shield W5100.
      4. Общение между 328P и 2560 идет по UART.
       
      Наилучший вариант, если кто-то откликнется из Киева, чтобы можно было вместе
      встретиться и поработать. Но если в Киеве никого нет, то куплю недорогой логический
      анализатор уровней и будем общаться удаленно. Готов к любому варианту.
    • By Nitro N
      Здравствуйте! 
      Дано: 
      BLE модуль JDY-10 Гироскоп + Акселерометр модуль GY-521  USB UART CH340G USB bluetooth адаптер Цель:
      передавать сигналы датчика гироскопа и акселерометра через bluetooth модуль на ПК. Мои рассуждения:
      Модуль GY-521 имеет I2C интерфейс, на JDY-10 стоит МК CC2541 который тоже имеет I2C интерфейс. Надо как-то их соединить и заставить отправлять показания на ПК. Как это сделать пока мне не понятно. Иногда приходят мысли что без перепрошивки контроллера не обойтись. 

      Подскажите пожалуйста как это реализовать?

      П.С. В идеале вообще конечно использовать один контроллер для снятия значений и отправки на ПК, но пока так. 
  • Сообщения

    • Да они такие и не нужны никому. Проблема решается так: 1. Поставь стабилитрон на 4,7в (у него почти нулевая зависимость напряжения от температуры). 2. Параллельно стабилитрону подключи подстроечный резистор на 47кОм. С движка переменного резистора сними напряжение 3,75в. Движок переменного резистора при этом поделит сопротивление 47 кОм в пропорции 37,5кОм + 9,5кОм, что при параллельном их соединении даст 7,58кОм. Т. е. будет почти равно сопротивлению параллельно соединенных резисторов 12кОм и 20кОм, которое составляет 7,50кОм. Таким образом одновременно будет подавлен температурный дрейф операционного усилителя из-за входных токов.
    • Pavlik Когда ты говоришь что "у него 2 обычных ам детектора" ты врешь и демонстрируешь нубство. Во первых ты разделил надвое узел что уже неверно, во вторых ты игнорируешь что ток у диодов общий и он не меняется, то есть схема с двумя диодами и двумя контурами это единая и не делимая схема. В третих ты игнорируешь отсутствие нагрузки у т.н. "АМ-детекторов,  в четвертых ты игнорируешь частоту среза цепочки авто-смещения 14Гц, через нее никак не смогут пройти колебания огибающей детекторов, в пятых ты игнорируешь факт что диоды работают с постоянным запирающим напряжением а АМ-детекторы так не работают. в шестых ты говорил что диод всегда открыт в седьмых ты отрицал импульсный ток диодов в восьмых ты запускал ток обратной полярности в диод ну и есть в 9-х в 10-х.. просто уже лень перечислять. Короче ты НУБ!
    • В принципе можно. Главное с грузом не переборщит. До 40-42гц понизить можно. Для 30-35гц Qts должна быть не выше 0.4. Доп магнитом в два раза не снизить т.к этот магнит лишь фокусирющий а не основной, который и задаёт в зазоре силу В. Фокусирующий "собирает в кучу" рассеиваемое магнитное поле, а не образует его.  Иногда (50/50) помогает перемагничивание магнитной системы т.к динамики после сборки намагничивают в индукторе. Но В зависит от качества (свойств) применяемого изготовителем ферромагнетика. 
    • А разве вы иначе думали? Задача следователя - найти улики, а задача прокурора - использовать их для обвинения. И только у суда задача во всем разобраться. Поэтому "все будет использовано против вас" Надеешься индульгенцию себе нализать?   Очередная заметка для дебилов: Не доехал котенок до бати походу...
    • не катит, только от ананасоФФ ...
    • Вот, например, - http://tel-spb.ru/lc.html. Они зависимы. Выбираете имеющийся конденсатор и для него вычисляете индуктивность. Или наоборот. А вообще, гугл в помощь -  https://www.google.com/search?q=расчет+колебательного+контура&oq=расчет+колебательного+контура
×
×
  • Create New...