Интерфейс I2C в настоящее время является одним из самых популярных интерфейсов для связи между внутренними компонентами электронной аппаратуры. Ранее на нашем сайте мы рассматривали статью про использование интерфейса I2C в плате Arduino, в которой демонстрировали возможность связи между двумя платами Arduino по протоколу I2C. В этой же статье мы заменим одну из плат Arduino в отмеченном проекте на плату STM32F103C8, которая также известна под названием STM32 Blue Pill ("синяя таблетка"). Таким образом, мы рассмотрим использование интерфейса I2C в плате STM32F103C8 (Blue Pill) и ее связь по данному интерфейсу с платой Arduino.
Если сравнивать интерфейс I2C (Inter Integrated Circuits) в платах STM32F103C8 Blue Pill и Arduino Uno мы увидим, что в плате Arduino он реализуется с помощью микроконтроллера ATMEGA328, а в плате STM32F103C8 – с помощью микроконтроллера ARM Cortex M3. Плата Arduino Uno имеет только одну шину I2C, а плате STM32 их две. К тому же по вычислительным возможностям плата STM32F103C8 значительно превосходит плату Arduino Uno.
В следующей статье на нашем сайте мы рассмотрим использование интерфейса SPI в плате STM32F103C8.
Контакты I2C в плате STM32F103C8
Распиновка платы STM32F103C8 представлена на следующем рисунке.
Как видно из представленной схемы, контактами I2C в плате STM32F103C8 являются следующие:
- SDA: PB7 или PB9, PB11;
- SCL: PB6 или PB8, PB10.
Контактами I2C в плате Arduino Uno являются:
- SDA: контакт A4;
- SCL: контакт A5.
Необходимые компоненты
- Плата разработки STM32F103C8 (STM32 Blue Pill) (купить на AliExpress).
- Плата Arduino Uno (купить на AliExpress).
- Светодиод – 2 шт. (купить на AliExpress).
- Кнопка – 2 шт.
- Резисторы 10 кОм (2 шт.) и 2,2 кОм (2 шт.) (купить на AliExpress).
- Макетная плата.
- Соединительные провода.
Схема проекта
Схема для связи между платами STM32F103C8T6 и Arduino Uno по интерфейсу I2C представлена на следующем рисунке.
Соединения между платами STM32 Blue Pill и Arduino Uno по шине I2C представлены в следующей таблице. Для этого требуется всего два провода.
Плата STM32 Blue Pill | Плата Arduino Uno | Описание контакта |
B7 | A4 | SDA |
B6 | A5 | SCL |
GND | GND | Ground |
Примечания:
- не забудьте соединить контакты GND (общий провод) плат Arduino и STM32F103C8;
- подключите подтягивающие резисторы 10 кОм к каждой из двух кнопок в схеме.
В нашем проекте плата STM32 Blue Pill будет выступать в роли ведущего устройства (Master) для связи по интерфейсу I2C, а плата Arduino – в роли ведомого (Slave). К каждой из плат подключены светодиод и кнопка.
Для демонстрации возможностей использования интерфейса I2C в плате STM32 мы будем управлять светодиодом, подключенным к ней (к ведущему), с помощью кнопки, подключенной к плате Arduino (ведомой). Аналогично мы будем управлять светодиодом, подключенным к плате Arduino (ведомой), с помощью кнопки, подключенной к плате STM32 (ведущей). Данные будут передаваться по шине I2C.
Объяснение программ для работы с интерфейсом I2C
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
Программа для платы STM32 в данном случае будет очень похожа на программу для платы Arduino – будет использоваться та же самая библиотека <wire.h>. Программировать плату STM32F103C8 мы будем с помощью Arduino IDE через USB порт, без использования внешнего FTDI программатора.
В нашем проекте у нас будет две программы: одна для платы STM32, которая будет выступать ведущим устройством, и одна для платы Arduino, которая будет выступать ведомым устройством.
Объяснение программы для платы STM32 (ведущей)
В программе для платы STM32 (ведущей) нам первым делом необходимо подключить библиотеки Wire.h и SoftWire.h.
1 2 |
#include <Wire.h> #include <SoftWire.h> |
Затем в функции void setup() мы инициализируем последовательную связь со скоростью 9600 бод.
1 |
Serial.begin(9600); |
После этого мы начнем связь по протоколу I2C на контактах (B6, B7).
1 |
Wire.begin(); |
Далее в функции void loop() мы будем получать данные от ведомой Arduino с помощью функции Wire.requestFrom(8,1), где 8 – это адрес ведомого (slave address), а 1 – количество запрашиваемых байт (то есть в данном случае мы будем запрашивать один байт).
1 |
Wire.requestFrom(8,1); |
Принимаемое значение мы будем считывать с помощью функции Wire.read().
1 |
byte a = Wire.read(); |
В зависимости от принятого от ведомого значения мы будем включать или выключать светодиод, подключенный к нашей плате STM32 (ведущей), с помощью функции digitalWrite(). Также мы будем выводить в окно монитора последовательной связи сообщение, свидетельствующее о включении/выключении светодиода.
1 2 3 4 5 6 7 8 9 10 |
if (a==1) { digitalWrite(LED,HIGH); Serial.println("Master LED ON"); } else { digitalWrite(LED,LOW); Serial.println("Master LED OFF"); } |
Затем мы будем считывать состояние контакта PA0 платы STM32, к которому подключена кнопка.
1 |
int pinvalue = digitalRead(buttonpin); |
Далее в зависимости от состояния контакта PA0 мы будем передавать соответствующее значение (0 или 1) ведомой плате Аrduino с адресом 8.
1 2 3 4 5 6 7 8 9 10 11 |
if(pinvalue==HIGH) { x=1; } else { x=0; } Wire.beginTransmission(8); Wire.write(x); Wire.endTransmission(); |
Объяснение программы для платы Arduino (ведомой)
Первым делом в программе подключим библиотеку Wire для использования возможностей протокола I2C.
1 |
#include <Wire.h> |
Затем в функции void setup() мы инициализируем последовательную связь со скоростью 9600 бод.
1 |
Serial.begin(9600); |
Далее начнем связь по протоколу I2C на контактах (A4, A5) с адресом ведомого (slave address) 8. Здесь важно указать адрес ведомого.
1 |
Wire.begin(8); |
После этого мы должны вызвать функцию Wire.onReceive когда ведомый получает значение от ведущего и функцию Wire.onRequest когда ведущий (Master) запрашивает значение от ведомого (Slave).
1 |
Wire.onReceive(receiveEvent); Wire.onRequest(requestEvent); |
Затем нам необходимо запрограммировать две функции – для события запроса и для события приема.
Для события запроса
Данная функция будет выполняться когда плата STM32 (ведущая) будет запрашивать значение от ведомого (slave). Эта функция будет считывать значение (0 или 1) с кнопки, подключенной к ведомой Arduino, и передавать его ведущей STM32 с помощью функции Wire.write().
1 2 3 4 5 6 7 8 9 10 11 12 13 |
void requestEvent() { int value = digitalRead(buttonpin); if (value == HIGH) { x=1; } else { x=0; } Wire.write(x); } |
Для события приема
Данная функция будет выполняться когда ведущий (Master) будет передавать данные ведомому (slave) с адресом равным 8. Функция будет считывать принятое от ведущего значение и сохранять его в переменной типа byte. Далее в зависимости от принятого значения она будет включать или выключать светодиод.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
void receiveEvent (int howMany) { byte a = Wire.read(); if (a == 1) { digitalWrite(LED,HIGH); Serial.println("Slave LED ON"); } else { digitalWrite(LED,LOW); Serial.println("Slave LED OFF"); } delay(500); } |
Тестирование работы проекта
Когда мы будем нажимать кнопку, подключенную к ведущей STM32, светодиод, подключенный к ведомой Arduino, будет загораться.
Если же мы нажмем кнопку на ведомой стороне, то загорится светодиод на ведущей стороне.
Когда обе кнопки будут нажаты одновременно, то оба светодиода загорятся в одно и то же время и будут оставаться во включенном состоянии до тех пор, пока мы не отпустим кнопки.
Более подробно работу проекта вы можете посмотреть на видео, приведенном в конце статьи.
Исходный код программы (скетча)
Код для ведущей STM32
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 |
//I2C Master Code (STM32F103C8) //I2C Communication between STM32 and Arduino //Circuit Digest #include<Wire.h> #include<SoftWire.h> //Library for I2C Communication functions #define LED PA1 #define buttonpin PA0 int x = 0; void setup() { Serial.begin(9600); //Begins Serial Communication at 9600 baud rate pinMode(buttonpin,INPUT); //Sets pin PA0 as input pinMode(LED,OUTPUT); //Sets PA1 as Output Wire.begin(); //Begins I2C communication at pin (B6,B7) } void loop() { Wire.requestFrom(8,1); // request bytes from slave arduino(8) byte a = Wire.read(); // receive a byte from the slave arduino and store in variable a if (a==1) //Logic to turn Master LED ON (if received value is 1) or OFF (if received value is 0) { digitalWrite(LED,HIGH); Serial.println("Master LED ON"); } else { digitalWrite(LED,LOW); Serial.println("Master LED OFF"); } { int pinvalue = digitalRead(buttonpin); //Reads the status of the pin PA0 if(pinvalue==HIGH) //Logic for Setting x value (To be sent to slave Arduino) depending upon inuput from pin PA0 { x=1; } else { x=0; } Wire.beginTransmission(8); // starts transmit to device (8-Slave Arduino Address) Wire.write(x); // sends the value x to Slave Wire.endTransmission(); // stop transmitting delay(500); } } |
Код для ведомой Arduino
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
//I2C Slave Code (Arduino) //I2C Communication between STM32 and Arduino //Circuit Digest #include<Wire.h> //Library for I2C Communication functions #define LED 7 #define buttonpin 2 byte x =0; void setup() { Serial.begin(9600); //Begins Serial Communication at 9600 baud rate pinMode(LED,OUTPUT); //Sets pin 7 as output Wire.begin(8); // join i2c bus with its slave Address as 8 at pin (A4,A5) Wire.onReceive(receiveEvent); //Function call when Slave Arduino receives value from master STM32 Wire.onRequest(requestEvent); //Function call when Master STM32 request value from Slave Arduino } void loop() { delay(100); } void receiveEvent (int howMany) //This Function is called when Slave Arduino receives value from master STM32 { byte a = Wire.read(); //Used to read value received from master STM32 and store in variable a if (a == 1) //Logic to turn Slave LED ON (if received value is 1) or OFF (if received value is 0) { digitalWrite(LED,HIGH); Serial.println("Slave LED ON"); } else { digitalWrite(LED,LOW); Serial.println("Slave LED OFF"); } delay(500); } void requestEvent() //This Function is called when Master STM32 wants value from slave Arduino { int value = digitalRead(buttonpin); //Reads the status of the pin 2 if (value == HIGH) //Logic to set the value of x to send to master depending upon input at pin 2 { x=1; } else { x=0; } Wire.write(x); // sends one byte of x value to master STM32 } } |
В текущий момент - ничего кроме неугомонной мысли. А на будущее - возможная потребность в ШИМ-выходах. В то время как РВ10/11 - обычные IN/OUT.
И еще гложет подозрение - не копируется ли опечатка:
SCL1 одновременно РВ6 и РВ8, аналогично и SDA.
А искал я в нашенском Яндексе, но каких только комбинаций (и поз) не перебирал...
Да и экзамен по английскому сдавал лет 40 назад.
А ардуиновские библиотеки - хитрее китайской грамоты, я поиском вообще не нашел внутри сочетания "SDA", "SCL".
Неужели на родине нет светлой головы, кто на пальцах разъяснит этот аспект?!
SCL1 одновременно РВ6 и РВ8 - насколько я понимаю можно использовать только один из этих контактов для работы по интерфейсу I2C, а вот контакт РВ10 - это уже SCL2, то есть годится для организации второго интерфейса I2C.
А ардуиновские библиотеки - хитрее китайской грамоты, я поиском вообще не нашел внутри сочетания "SDA", "SCL" - по номерам контактов искать не пробовали? Они в начале программы могут с помощью define переопределяться на какие нибудь человеческие названия.
Неужели на родине нет светлой головы - так уехали же большинство светлых голов на "загнивающий запад", наша же власть не ценит интеллектуальный потенциал своих подданных, а ценит только нефть и газ ))
"Как видно из представленной схемы, контактами I2C в плате STM32F103C8 являются следующие:
SDA: PB7 или PB9, PB11;
SCL: PB6 или PB8, PB10."
Нигде не нашел, как нужно указать, что I2C-дисплей подключён к контактам 10-11.
Все только из статьи в статью передирают друг у дружки.
Помогите, отчаялся найти информацию!
PS А STM-ка начала программироваться только со свистка, год на USB потерял...
Ну а что мешает вам подключить I2C-дисплей к контактам 6 и 7 как в статье?
Попробовал поискать информацию по вашему запросу, arduino.ru/forum/programmirovanie/i2c-stm32f103 - здесь указано что в поисковиках надо искать по запросу STM32F103C8 i2c2.
Гугл, к примеру, по этому запросу выдает:
community.st.com/t5/stm32-mcus-products/i2c-communication-problem-with-stm32f103c8-blue-pill-board/td-p/89481
sparklogic.ru/stm32f103-boards/solved-i2c2-on-stm32f103c8t6-and-hwire.html?utm_referrer=https%3A%2F%2Fwww.google.com%2F
Не пробовали эти ресурсы?
Добрый день!
https://github.com/stm32duino/Arduino_Core_STM32/blob/main/libraries/Wire/examples/i2c_scanner/i2c_scanner.ino
^ вот это порекомендовали на Форуме (~
#include
#include
LiquidCrystal_I2C lcd(0x27, 16, 2);
void setup(){
Wire.setSDA(PB11);
Wire.setSCL(PB10);
Wire.begin();
...
})
но компилятор ругается. Т.е. пока получается как при любой власти: "Имеете право, но не можете!.."
Добрый вечер. Ну не унывайте, настанет светлый день и найдете то, что вам нужно ))
Если совсем не идет программный метод задать линии данных(SDA) и линии тактирования(SCL), порекомендую просто скачать CubeMX и в графическом интерфейсе этого приложения явно задать пины(Путь Connectivity->I2C1->Enabled). После чего в проджект можно вывести как сам Makefile так и сборку для любого другого ide
Хорошо, спасибо за конструктивный комментарий для нашего сайта