Jump to content

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


G1KuL1N
 Share

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; // вернуть считанное значение
}

 

Я собрал Маршалл из деталей телевизора Весна, а он звучит не как Маршалл, а как Весна. В чем может быть проблема?

Кто-то куёт Metal, а я паяю Industrial © G1KuL1N (А то уже по всему интернету растащили :)

Link to comment
Share on other sites

Реклама: ООО ТД Промэлектроника, ИНН: 6659197470, Тел: 8 (800) 1000-321

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

Нужно делать то, что нужно. А то, что не нужно, делать не нужно. (С) Винни Пух

Link to comment
Share on other sites

Реле, разъемы, вентиляторы, корпуса, микросхемы, конденсаторы и много другое!

Скидки до 20% на сайте электронного дискаунтера ТМ Электроникс.

Бесплатная доставка в любую точку России + кэшбэк 15%.

Подробнее

Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849

Материалы вебинара «Литиевые аккумуляторы EVE Energy и решения для управления перезаряжаемыми источниками тока»

Опубликованы материалы вебинара Компэл, посвященного литиевым аккумуляторам EVE Energy и решениям для управления перезаряжаемыми источниками тока.

На вебинаре мы представили информацию не только по линейкам аккумуляторной продукции EVE, но и по решениям для управления ею, что поможет рассмотреть эти ХИТ в качестве дополнительной альтернативы для уже выпускающихся изделий. Также рассмотрели нюансы работы с производителем и сервисы, предоставляемые Компэл по данной продукции. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

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

Я собрал Маршалл из деталей телевизора Весна, а он звучит не как Маршалл, а как Весна. В чем может быть проблема?

Кто-то куёт Metal, а я паяю Industrial © G1KuL1N (А то уже по всему интернету растащили :)

Link to comment
Share on other sites

Комплексные решения с литиевыми батареями EVE для интеллектуальных систем безопасности

Литиевые химические источники тока EVE по своим характеристикам являются идеальным выбором для современных интеллектуальных систем безопасности. EVE разрабатывает литиевые батареи, опираясь на многолетние исследования таких областей применения, как беспроводная связь стандартов Bluetooth, LoRa, Wi-Fi, NB-IoT и ZigBee. Рассмотрим решения для систем безопасности с использованием батарей EVE. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

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

Link to comment
Share on other sites

Литиевые батарейки и аккумуляторы от мирового лидера  EVE в Компэл

Компания Компэл, официальный дистрибьютор EVE Energy, бренда №1 по производству химических источников тока (ХИТ) в мире, предлагает продукцию EVE как со склада, так и под заказ. Компания EVE широко известна в странах Европы, Америки и Юго-Восточной Азии уже более 20 лет. Недавно EVE была объявлена поставщиком новых аккумуляторных элементов круглого формата для электрических моделей «нового класса» компании BMW.

Продукция EVE предназначена для самого широкого спектра применений – от бытового до промышленного. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

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

Edited by mail_robot

Нужно делать то, что нужно. А то, что не нужно, делать не нужно. (С) Винни Пух

Link to comment
Share on other sites

33 минуты назад, mail_robot сказал:

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

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

Link to comment
Share on other sites

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

Edited by MasterElectric
Link to comment
Share on other sites

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

Я собрал Маршалл из деталей телевизора Весна, а он звучит не как Маршалл, а как Весна. В чем может быть проблема?

Кто-то куёт Metal, а я паяю Industrial © G1KuL1N (А то уже по всему интернету растащили :)

Link to comment
Share on other sites

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

Я собрал Маршалл из деталей телевизора Весна, а он звучит не как Маршалл, а как Весна. В чем может быть проблема?

Кто-то куёт Metal, а я паяю Industrial © G1KuL1N (А то уже по всему интернету растащили :)

Link to comment
Share on other sites

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

Нужно делать то, что нужно. А то, что не нужно, делать не нужно. (С) Винни Пух

Link to comment
Share on other sites

  • 7 months later...
В 13.08.2018 в 10:55, G1KuL1N сказал:

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

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

Что может быть лучше в радиоэлектронике, чем программирование микроконтроллеров ?

Link to comment
Share on other sites

  • 9 months later...
Guest Алексей Ададуров

Столкнулся с тем, что в STM32F103С8T6 и STM32F10RBT6 при работе с I2C через HAL начинает глючить USB. Думаю попробовать прикрутить софтовую реализацию -- и нашел вашу. Большое спасибо!

Link to comment
Share on other sites

  • 2 months later...
Guest Roman

Переплутал SDA/SCL линии в плате прототипа, а на исправление нужно много времени, пришлось искать обходной путь. Софтварная реализация спасла меня :)

Link to comment
Share on other sites

Join the conversation

You can post now and register later. 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
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
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...
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...