Все мы знаем, что микроконтроллеры могут работать только с цифровыми значениями, но в реальной жизни нам приходится иметь дело и с аналоговыми сигналами. Поэтому для преобразования цифровых значений, обрабатываемых микроконтроллерами, в аналоговые сигналы, применяются устройства, называемые цифро-аналоговыми преобразователями (ЦАП, в англ. DAC – Digital to Analog Converter).
Простым примером использования АЦП (аналого-цифрового преобразования) и ЦАП, является запись и воспроизведение какой либо песни. Сначала звуки песни, поступающие от микрофона оцифровываются с помощью АЦП и затем хранятся в цифровой форме (в виде файла). Затем для воспроизведения этой песни из цифрового формата используется ЦАП, который преобразует цифровые данные в аналоговый звуковой сигнал.
У платы STM32F103C8 есть 10 входов с АЦП (их использование мы рассматривали в этой статье), но встроенного цифро-аналогового преобразователя (ЦАП) у ней нет, поэтому в данной статье мы рассмотрим подключение внешнего модуля ЦАП MCP4725 к плате STM32F103C8 (Blue Pill). Ранее на нашем сайте мы рассматривали подключение данного модуля ЦАП к платам Arduino и Raspberry Pi.
Необходимые компоненты
- Плата разработки STM32F103C8 (STM32 Blue Pill) (купить на AliExpress).
- Микросхема ЦАП MCP4725 (купить на AliExpress).
- ЖК дисплей 16x2 (купить на AliExpress).
- Потенциометр 10 кОм (купить на AliExpress).
- Макетная плата.
- Соединительные провода.
Модуль ЦАП MCP4725
MCP4725 представляет собой микросхему модуля ЦАП с разрешением 12 бит и предназначен для формирования аналогового напряжения в диапазоне от 0 до 5V. Управляется модуль MCP4725 по интерфейсу I2C. Также модуль содержит энергонезависимую память EEPROM.
Модуль MCP4725 имеет разрешение (разрешающую способность) 12 бит. Это означает что на вход модуля мы можем подавать цифровые значения в диапазоне от 0 до 4096. На выходе модуля в зависимости от значения на его входе будет формироваться аналоговое напряжение, зависящее от входного значения и значения опорного напряжения. Максимальное опорное напряжение для модуля составляет 5V.
Формула для расчета выходного напряжения модуля выглядит следующим образом:
O/P Voltage = (Reference Voltage / Resolution) x Digital Value
где Reference Voltage – опорное напряжение,
Resolution – разрешение (в нашем случае 4096),
Digital Value – цифровое значение на входе модуля MCP4725.
К примеру, если в качестве опорного напряжения мы используем 5V, а на входе модуля у нас значение 2048, то на выходе модуля у нас будет напряжение:
O/P Voltage = (5/ 4096) x 2048 = 2.5V
Распиновка модуля MCP4725
Назначение контактов (распиновка) модуля MCP4725 представлена на следующем рисунке.
Контакты ЦАП MCP4725 | Их назначение |
OUT | выходное аналоговое напряжение |
GND | земля для выходного аналогового напряжения |
SCL | линия синхронизация протокола I2C |
SDA | линия передачи данных протокола I2C |
VCC | входное опорное напряжение 5V или 3.3V |
GND | земля для опорного напряжения |
Далее рассмотрим использование интерфейса I2C в модуле.
Связь с модулем MCP4725 по интерфейсу I2C
Модуль MCP4725 можно подключить практически к любому современному микроконтроллеру по интерфейсу I2C. Связь по интерфейсу I2C требует всего 2-х линий (проводов) – SCL и SDA.
По умолчанию I2C адрес модуля MCP4725 равен 0x60. Также возможны адреса 0x61 или 0x62
Распиновка платы STM32 Blue Pill приведена на следующем рисунке.
Контакты I2C в плате STM32F103C8:
SDA: PB7 или PB9, PB11.
SCL: PB6 или PB8, PB10.
Более подробно об использовании интерфейса I2C в плате STM32F103C8 можно прочитать в этой статье.
Схема проекта
Схема подключения модуля ЦАП MCP4725 к плате STM32F103C8 (Blue Pill) представлена на следующем рисунке.
Соединения между платой STM32F103C8 и ЖК дисплеем представлены в следующей таблице.
№ контакта ЖК дисплея | Обозначение контакта ЖК дисплея | Контакт платы STM32 |
1 | Ground (Gnd) | Ground (G) |
2 | VCC | 5V |
3 | VEE | средний контакт потенциометра |
4 | Register Select (RS) | PB11 |
5 | Read/Write (RW) | Ground (G) |
6 | Enable (EN) | PB10 |
7 | Data Bit 0 (DB0) | No Connection (NC) |
8 | Data Bit 1 (DB1) | No Connection (NC) |
9 | Data Bit 2 (DB2) | No Connection (NC) |
10 | Data Bit 3 (DB3) | No Connection (NC) |
11 | Data Bit 4 (DB4) | PB0 |
12 | Data Bit 5 (DB5) | PB1 |
13 | Data Bit 6 (DB6) | PC13 |
14 | Data Bit 7 (DB7) | PC14 |
15 | LED Positive | 5V |
16 | LED Negative | Ground (G) |
Соединения между платой STM32F103C8 и модулем ЦАП MCP4725 представлены в следующей таблице.
Модуль ЦАП MCP4725 | Плата STM32F103C8 | Мультиметр |
SDA | PB7 | NC |
SCL | PB6 | NC |
OUT | PA1 | Positive Probe |
GND | GND | Negative Probe |
VCC | 3.3V | NC |
Средний контакт потенциометра подключен к аналоговому контакту PA0 платы STM32F103C8, левый контакт потенциометра подключен к контакту GND платы, а его правый контакт – к контакту 3.3V платы.
В данном проекте мы подключим модуль ЦАП MCP4725 к плате STM32 и будем использовать потенциометр для подачи аналогового значения напряжения на контакт PA0 платы STM32. Далее с помощью АЦП на данном контакте мы будем преобразовывать аналоговое значение напряжения в цифровую форму. После этого мы будем передавать это значение напряжения на модуль MCP4725 по интерфейсу I2C. Затем модуль MCP4725 будет конвертировать это цифровое значение в аналоговое и после этого мы будем использовать еще один контакт АЦП PA1 платы STM32 чтобы проверить точность работы аналогового выхода (точность преобразования) модуля MCP4725. После всего этого мы будем выводить на экран ЖК дисплея значения аналогового напряжения на входе контакта PA0 платы STM32 и на выходе модуля MCP4725.
Внешний вид собранной конструкции проекта показан на следующем рисунке.
Объяснение программы для платы STM32
Полный код программы приведен в конце статьи, здесь же мы кратко рассмотрим его основные фрагменты.
Первым делом в программе подключим библиотеки для работы с интерфейсом I2C (wire.h, SoftWire.h) и ЖК дисплеем (liquidcrystal.h).
1 2 3 |
#include<Wire.h> #include <LiquidCrystal.h> #include<SoftWire.h> |
Далее укажем контакты платы STM32F103C8, к которым подключен ЖК дисплей, и создадим объект для работы с дисплеем.
1 2 |
const int rs = PB11, en = PB10, d4 = PB0, d5 = PB1, d6 = PC13, d7 = PC14; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); |
Затем укажем I2C адрес модуля MCP4725, по умолчанию он равен 0x60.
1 |
#define MCP4725 0x60 |
Затем в функции void setup() инициализируем связь по протоколу I2C на контактах PB7 (SDA) и PB6 (SCL) платы STM32F103C8.
1 |
Wire.begin(); //Begins the I2C communication |
Далее укажем тип ЖК дисплея (16x2) и покажем на его экране приветственное сообщение.
1 2 3 4 5 6 7 8 9 10 |
lcd.begin(16,2); lcd.print("CIRCUIT DIGEST"); delay(1000); lcd.clear(); lcd.setCursor(0,0); lcd.print("STM32F103C8"); lcd.setCursor(0,1); lcd.print("DAC with MCP4725"); delay(2000); lcd.clear(); |
Затем в функции void loop() в buffer[0] запишем значение контрольного байта (значение 010 устанавливает модуль MCP4725 в режим записи).
1 |
buffer[0] = 0b01000000; |
После этого мы считаем аналоговое значение напряжение с контакта PA0, преобразуем его в цифровое значение в диапазоне от 0 до 4096 (поскольку АЦП имеет разрешение 12 бит) и сохраним его в переменной adc.
1 |
adc = analogRead(PA0) ; |
Далее на основе считанного цифрового значения рассчитаем значение аналогового напряжения на контакте PA0 с учетом того, что опорное напряжение составляет у нас величину равную 3.3V.
1 |
float ipvolt = (3.3/4096.0)* adc; |
Затем запишем значения наиболее значащих бит в buffer[1] при помощи сдвига на 4 разряда вправо переменной adc, а значения наименее значащих бит запишем в buffer[2] при помощи сдвига на 4 разряда влево переменной adc.
После этого считаем значение с выхода АЦП контакта PA1 платы STM32 – к этому контакту подключен выход модуля ЦАП MCP4725. Также к этому контакту можно подключить мультиметр чтобы проверить величину выходного напряжения с выхода ЦАП.
1 |
unsigned int analogread = analogRead(PA1); |
Затем на основе значения переменной analogread рассчитаем величину аналогового напряжения на контакте PA1.
1 |
float opvolt = (3.3/4096.0)* analogread; |
Также в функции void loop() мы начнем передачу данных модулю MCP4725:
1 |
Wire.beginTransmission(MCP4725); |
Далее передадим контрольный (управляющий) байт по интерфейсу I2C:
1 |
Wire.write(buffer[0]); |
Затем передадим наиболее значащие биты (MSB) по интерфейсу I2C.
1 |
Wire.write(buffer[1]); |
После чего передадим наименее значащие биты (MSB) по интерфейсу I2C.
1 |
Wire.write(buffer[2]); |
После этого закончим передачу.
1 |
Wire.endTransmission(); |
Затем выведем полученные значения на экран ЖК дисплея с помощью функции lcd.print().
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
lcd.setCursor(0,0); lcd.print("A IP:"); lcd.print(adc); lcd.setCursor(10,0); lcd.print("V:"); lcd.print(ipvolt); lcd.setCursor(0,1); lcd.print("D OP:"); lcd.print(analogread); lcd.setCursor(10,1); lcd.print("V:"); lcd.print(opvolt); delay(500); lcd.clear(); |
Тестирование работы проекта
Когда схема проекта будет готова, а программа загружена в плату 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 58 59 60 61 62 63 64 65 66 |
#include<Wire.h> //Include Wire library for using I2C functions #include<SoftWire.h> #include <LiquidCrystal.h> //Include LCD library for using LCD display functions #define MCP4725 0x60 //MCP4725 address as 0x60 Change yours accordingly const int rs = PB11, en = PB10, d4 = PB0, d5 = PB1, d6 = PC13, d7 = PC14; LiquidCrystal lcd(rs, en, d4, d5, d6, d7); unsigned int adc; byte buffer[3]; void setup() { Wire.begin(); //Begins the I2C communication lcd.begin(16,2); //Sets LCD in 16X2 Mode lcd.print("CIRCUIT DIGEST"); delay(1000); lcd.clear(); lcd.setCursor(0,0); lcd.print("STM32F103C8"); lcd.setCursor(0,1); lcd.print("DAC with MCP4725"); delay(2000); lcd.clear(); } void loop() { buffer[0] = 0b01000000; //Sets the buffer0 with control byte (010-Sets in Write mode) adc = analogRead(PA0); //Read Analog value from pin PA0 float ipvolt = (3.3/4096.0)* adc; //Finding voltage formula buffer[1] = adc >> 4; //Puts the most significant bit values buffer[2] = adc << 4; //Puts the Least significant bit values unsigned int analogread = analogRead(PA1) ; //Reads analog value from PA1 float opvolt = (3.3/4096.0)* analogread; //Finding Voltage Formula Wire.beginTransmission(MCP4725); //Joins I2C bus with MCP4725 with 0x60 address Wire.write(buffer[0]); //Sends the control byte to I2C Wire.write(buffer[1]); //Sends the MSB to I2C Wire.write(buffer[2]); //Sends the LSB to I2C Wire.endTransmission(); //Ends the transmission lcd.setCursor(0,0); lcd.print("A IP:"); lcd.print(adc); //Prints the ADC value from PA0 lcd.setCursor(10,0); lcd.print("V:"); //Prints the Input Voltage at PA0 lcd.print(ipvolt); lcd.setCursor(0,1); lcd.print("D OP:"); lcd.print(analogread); //Prints the ADC value from PA1 (From DAC) lcd.setCursor(10,1); lcd.print("V:"); lcd.print(opvolt); //Prints the Input Voltage at PA1 (From DAC) delay(500); lcd.clear(); } |