import {Tangle as Tangle} from "@mbostock/tangle"
import {texmd as texmd} from "@kelleyvanevert/katex-within-markdown"
По окончании занятия учащиеся смогут…
Соберите схему с фоторезистором для измерения уровня освещённости. Полученные значения выводите на экран.
и резистор на 10 кОм образуют делитель напряжения. Напряжение считывается из точки между двумя сопротивлениями и подаётся на аналоговый пин A0. Аналогово-цифровой преобразователь поможет привести показания с датчика к целым значениям в диапазоне от 0 до 1023.
import {Tangle as Tangle} from "@mbostock/tangle"
import {texmd as texmd} from "@kelleyvanevert/katex-within-markdown"
viewof r1 = Inputs.input(480)
viewof r2 = Inputs.input(480)
viewof U = Inputs.input(5)
Uout = U*(r2/(r1+r2))Выходное напряжение можно измерить по следующей формуле:
texmd`Если входное напряжение $U_{in}$ равно <nobr>${Inputs.bind(Tangle({min: 0, max:12, minWidth: "2em"}), viewof U)} В</nobr>, а сопротивления резисторов <nobr>$R_1=$ ${Inputs.bind(Tangle({min: 220, step: 20, max: 10000, minWidth: "2em"}), viewof r1)} Ом</nobr> и <nobr>$R_1=$ ${Inputs.bind(Tangle({min: 220, max: 10000, step: 20, minWidth: "2em"}), viewof r2)} Ом</nobr>, то выходное напряжение $U_{out}$ будет равно **<nobr>${Uout.toFixed(1).toLocaleString("ru")} В</nobr>**.`Если входное напряжение Uin равно
md`Убедиться в этом можно, подставив значения в формулу выше: ${tex`U_{out}=${U}\ В\cdot\frac{${r2}\ Ом}{${r1}\ Ом + ${r2}\ Ом}=${Uout.toFixed(1)}\ В`}
`
svg`<svg width="{width}" height="150px" version="1.1" viewBox="0 15 80 30" xmlns="http://www.w3.org/2000/svg">
<rect x="19.92" y="26.254" width="11.237" height="4.4949" style="fill:none;stroke-width:.26458;stroke:#000"/>
<rect x="50.567" y="26.254" width="11.237" height="4.4949" style="fill:none;stroke-width:.26458;stroke:#000"/>
<path d="m19.92 28.501h-12.157" style="fill:none;stroke-width:.26458;stroke:#000"/>
<path d="m31.157 28.501h19.41" style="fill:none;stroke-width:.26458;stroke:#000"/>
<path d="m61.804 28.501h9.194" style="fill:none;stroke-width:.26458;stroke:#000"/>
<path d="m70.998 26.122v4.7594" style="fill:none;stroke-width:.26458;stroke:#000"/>
<path d="m71.923 26.799v3.2506" style="fill:none;stroke-width:.26458;stroke:#000"/>
<path d="m72.847 27.454v2.0948" style="fill:none;stroke-width:.26458;stroke:#000"/>
<path d="m7.7638 26.876v3.2506" style="fill:none;stroke-width:.26458;stroke:#000"/>
<path d="m40.862 28.501v-8.6577" style="fill:none;stroke-width:.26458;stroke:#000"/>
<path d="m39.887 16.134h1.9503v2.4453l-0.97517 1.2642-0.97517-1.2642z" style="fill:none;stroke-width:.26458;stroke:#000"/>
<text x="42.763126" y="18.600513" style="fill:none;font-size:3.175px;stroke-width:.1;stroke:#000000" xml:space="preserve"><tspan x="42.763126" y="18.600513" font-weight="bold" style="fill:#000000;stroke-width:.1;stroke:none">${Uout.toFixed(1)} В</tspan></text>
<text x="23.83396" y="34.195797" style="fill:none;font-size:3.175px;stroke-width:.1;stroke:#000000" xml:space="preserve"><tspan x="23.83396" y="34.195797" style="fill:#000000;stroke-width:.1;stroke:none">R1=${r1} Ом</tspan></text>
<text x="54.218739" y="34.211674" style="fill:#000000;font-size:3.175px;stroke-width:.1" xml:space="preserve"><tspan x="54.218739" y="34.211674" style="stroke-width:.1">R2=${r2} Ом</tspan></text>
<text x="2.0447423" y="25" style="fill:none;font-size:3.175px;stroke-width:.1;stroke:#000000" xml:space="preserve"><tspan x="2.0447423" y="25" style="fill:#000000;stroke-width:.1;stroke:none">${U} В</tspan></text>
</svg>`Убедиться в этом можно, подставив значения в формулу выше: Uout=5 В⋅480 Ом+480 Ом480 Ом=2.5 В
Если сопротивление первого резистора зафиксировать, то выходное напряжение будет зависеть только от сопротивления второго резистора.
Значение измеренного напряжения - величина непрерывная. Микроконтроллер может обрабатывать лишь дискретные значения. Для преобразования аналогового сигнала в цифровой используется аналогово-цифровой преобразователь (АЦП).
md`АЦП характеризуется своей разрядностью *n*. АЦП вернёт значения от ${tex`0`} до ${tex`2^{n}-1`}. Например, если разрядность АЦП равна ${Inputs.bind(Tangle({min: 2, max: 10, minWidth: "2em"}), viewof b)}, то максимальное значений на выходе АЦП равно ${tex`2^{${b}}-1=${Math.pow(2, b)-1}`}.`АЦП характеризуется своей разрядностью n. АЦП вернёт значения от 0 до 2n−1. Например, если разрядность АЦП равна 3, то максимальное значений на выходе АЦП равно 23−1=7.
viewof b = Inputs.input(3)
viewof bits = Inputs.input(3)
viewof Vref = Inputs.input(5)
viewof Vi = Inputs.input(3)Опорное напряжение
Цифровое значение на выходе АЦП определяется по следующей формуле:
md`Например, если разрядность АЦП ${Inputs.bind(Tangle({min: 3, max: 10, minWidth: "2em"}), viewof bits)} бита, опорное напряжение равно <span style="color: #008000">${Inputs.bind(Tangle({min: 1, max: 5, value:5, step: 0.1, minWidth: "2em"}), viewof Vref)} В</span>, а измеренное напряжение равно <span style="color: #FF1D1D">${Inputs.bind(Tangle({min: 0, max: 12, step: 0.1, value:5, minWidth: "2em"}), viewof Vi)} В</span>, то значение на выходе АЦП равно <span style="color: #0000FF">${Vi > Vref ? Math.pow(2, bits)-1 : Math.round(Vi/Vref*(Math.pow(2, bits)-1))}</span>.`Например, если разрядность АЦП 3 бита, опорное напряжение равно 5 В, а измеренное напряжение равно 3 В, то значение на выходе АЦП равно 4.
Plot.plot({
x: {grid: true, label: "время, c"},
y:{domain:[0,5], label: "Напряжение, В"},
marks: [
Plot.ruleY([0]),
Plot.ruleY([Vref], {label: "hello", stroke: "green", strokeWidth: 2}),
Plot.lineY(plotData,{x: "x", y: "y", stroke: "red", strokeWidth: 2}),
]
})
Plot.plot({
x: {grid: true, label: "время, c"},
y:{domain:[0, Math.pow(2, bits)-1], label: "Значение на выходе АЦП"},
marks: [
Plot.ruleY([0]),
Plot.lineY(adcResult,{x: "x", y: "y", stroke: "blue", strokeWidth: 2} ),
]
})x = d3.range(0, 10, 0.01)
y = [...x.map(i=>Math.sin(i*2) + Math.cos(0.5*i)+2)]
plotData = x.map(function(val, index){
return { x: val, y: y[index] };
});
rangeConverterADC = d3.scaleQuantize([0, Vref], d3.range(0, Math.pow(2, bits), 1));
adcResult = {
let temp = []
for (let index = 0; index < x.length; index=index+2) {
temp.push(rangeConverterADC(y[index]));
}
let extendedTemp = []
temp.forEach(item=>{
extendedTemp.push(item);
extendedTemp.push(item);
})
let data = x.map(function(val, index){
return { x: index / 100, y: extendedTemp[index] };
});
return data
}Переменная val хранит сырые показания датчика, полученные напрямую с пина A0. В переменной normalVal сохраним значение с датчика приведённое к диапазону 0..100.
// Чтение аналоговых датчиков
// информационный пин фоторезистора назовём LDR_PIN
// и будем ссылаться на аналоговый пин A0
#define LDR_PIN A0
int val; // хранение сырых показаний с датчика 0..1023
int normalVal; // хранение показаний приведённых к 0..100
void setup()
{
// Начинаем связь через последовательный порт
Serial.begin(9600);
}
void loop()
{
// сохраняем показания с аналогового пина A0
val = analogRead(LDR_PIN);
/*
С помощью функции map приводим показания с фоторезистора
val из диапазона 0..1023 к диапазону 0..100 и сохраняем
результат в переменную normalVal
*/
normalVal = map(val, 0, 1023, 0, 100);
/*
Выводим информацию в монитор последовательного порта
print - выводит в текущую строку
println - вывод и перенос на новую строку
Ответ выводим в формате:
val, normalVal
*/
Serial.print(val);
Serial.print(", ");
Serial.println(normalVal);
}Для обмена информацией между Arduino-скетчем и компьютером используем библиотеку Serial. Чтобы посмотреть сообщения в Arduino IDE, выберите пункт меню Инструменты - Монитор порта ⌃Ctrl + ⇧Shift + M Плата должна быть подключена к компьютеру. При моделировании в TinkerCad, откройте вкладку Код и нажмите по кнопке Монитор последовательного интерфейса внизу окна.
Чтобы преобразовать один диапазон значений в другой, используем функцию map(значение, текущий_минимум, текущий_максимум, новый_минимум, новый_максимум).
Соберите схему с фоторезистотором и пьезодинамиком. Используйте показания фоторезистора для того, чтобы воспроизводить звуки различной частоты, собрав и запрограммировав устройство, похожее на терменвокс.
Схема этого задания основана на предыдущем. Можете добавить пьезодинамик к уже собранной схеме.
// Терменвокс
// Пин управления пьезодинамиком
#define BUZZER_PIN 3
// Пин фоторезистора
#define LDR_PIN A0
void setup()
{
// Пин пьезодинамика настраиваем на выход
// При этом пин должен поддерживать ШИМ
pinMode(BUZZER_PIN, OUTPUT);
}
void loop()
{
// val - хранение показаний с фоторезистора
// freq - вычисленная частота звука
int val, freq;
// считываем уровень освещённости - 0..1023
val = analogRead(LDR_PIN);
// Расчитываем частоту звука. Преобразуем диапазон
// 0..1023 в диапазон частот от 250 до 500 Гц.
freq = map(val, 0, 1023, 250, 500);
// Воспроизводим звук с частотой freq в течении 20 миллисекунд
tone(BUZZER_PIN, freq, 20);
}Составьте программу, которая при нажатии на кнопку, будет увеличивать яркость светодиода.
Резистор на 10 кОм в схеме называется стягивающим. Он тянет значение на 8 пине к земле, пока кнопка не нажата.
Если нужно, чтобы в не нажатом состоянии на пине с кнопкой была логическая единица (5 В), то кнопку подключают к питанию через подтягивающий резистор, который подтягивает значение на пине к напряжению 5 В.
Для регулирования яркости светодиода через цифровой выход, используем Широтно-импульсную модуляцию. Путём быстрого включения и выключения напряжения на цифровом выходе можно симулировать подачу аналогового усреднённого значения на вход светодиода.
md`Значение усредненного напряжения зависит от соотношения времени, когда значение на выходе пина равно 0 или 5 В. Если значение коэффициента заполнения равно ${Inputs.bind(Tangle({min: 0, max: 100, minWidth: "2em"}), viewof dutyCicle)}%, то среднее значение напряжения на контактах светодиода будет равно **<span style="color: #E64848">${Vout.toFixed(1)} В</span>** при максимально возможных 5 В. Это значит, что *${pwmResult}*.`Значение усредненного напряжения зависит от соотношения времени, когда значение на выходе пина равно 0 или 5 В. Если значение коэффициента заполнения равно 50%, то среднее значение напряжения на контактах светодиода будет равно 2.5 В при максимально возможных 5 В. Это значит, что светодиод будет гореть ярко.
Plot.plot({
x: {label: "Время, с"},
y:{domain:[0,5], label: "Напряжение, В"},
height: 200,
marks: [
Plot.ruleY([0]),
Plot.ruleY([Vout], {strokeWidth: 2, stroke: "#E64848"}),
Plot.lineY(seq, {x: (d,i) => i, stroke: "#2192FF", strokeWidth: 2}),
],
})
svg`<svg width="{width}" height="72px" viewBox="0 0 {width} 72" id="emoji" xmlns="http://www.w3.org/2000/svg">
<g id="color">
<path fill="hsl(201deg 100% ${rangeConverter(dutyCicle)}%)" stroke="none" d="M31.3882,26.7177c0,0,9.2367-1.8188,8.4221-9.1964c-1.3538-12.261-1.4678-10.4237-1.4678-10.4237 l-5.5293,1.0104C32.8133,8.1081,35.9998,21.7018,31.3882,26.7177z"/>
<path fill="hsl(201deg 100% ${rangeConverter(dutyCicle)}%)" stroke="none" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M34.5417,7.0359c-8.1462,0-14.75,7.496-14.75,16.7427v16.388h29.5"/>
<rect x="26.8333" y="44.5" width="4" height="22.095" fill="#d0cfce" stroke="none" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2"/>
<rect x="41.3333" y="44.5" width="4" height="16.4792" fill="#d0cfce" stroke="none" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2"/>
<path fill="hsl(201deg 100% ${rangeConverter(dutyCicle)}%)" stroke="none" d="M34.5417,7.5625c0,0,15.3232,0.5495,15.9047,13.875c0.9664,22.1458,0.0665,18.9191,0.0665,18.9191 l-9.3254-0.19C41.1875,40.1667,42.6247,15.125,34.5417,7.5625z"/>
<rect x="43.3333" y="40.7917" width="11.8333" height="3.0833" fill="#61b2e4" stroke="none"/>
<rect x="16.3353" y="40.7917" width="26.998" height="3.0833" fill="#92d3f5" stroke="none"/>
</g>
<g id="hair"/>
<g id="skin"/>
<g id="skin-shadow"/>
<g id="line">
<path fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M34.5417,7.0359c-8.1462,0-14.75,7.496-14.75,16.7427v16.388h29.5"/>
<rect x="26.8333" y="44.5" width="4" height="22.095" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2"/>
<rect x="41.3333" y="44.5" width="4" height="16.4792" fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2"/>
<path fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M25.8125,19.0625"/>
<path fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" d="M35.2497,7.0359c8.1462,0,14.75,7.496,14.75,16.7427v7.388"/>
<polygon fill="none" stroke="#000000" stroke-linecap="round" stroke-linejoin="round" stroke-miterlimit="10" stroke-width="2" points="16,44.5 45.5309,44.5 45.9063,44.5 56,44.5 56,40.1667 45.9063,40.1667 45.4999,40.1667 16,40.1667"/>
</g>
</svg>`viewof dutyCicle = Inputs.input(50)
viewof freq = Inputs.range([2, 20], {label: "Частота", step: 1, value: 5})
rangeConverter = d3.scaleQuantize([0, 100], d3.range(30, 95));seq = {
let arr = Array(1000).fill(0)
const period = Math.round(1000 / freq)
const highCount = Math.round(dutyCicle * period / 100)
const lowCount = period - highCount
let onePeriodArray = Array(highCount).fill(5)
onePeriodArray.push(...Array(lowCount).fill(0))
let fullSec = []
for (let index = 0; index < 50; ++index) {
fullSec.push(...onePeriodArray)
}
return fullSec.slice(0, 1000)
}
Vout = 5 * dutyCicle / 100pwmResult = {
let s = ""
if(dutyCicle<=0)
s = "светодиод перестанет гореть"
if(dutyCicle>0 && dutyCicle< 50)
s = "светодиод будет гореть тусклo"
if(dutyCicle>=50 && dutyCicle < 99)
s = "светодиод будет гореть ярко"
if(dutyCicle === 100)
s = "светодиод будет гореть максимально ярко"
return s
}Чем больше частота, тем менее заметным станет мигание светодиода во время его периодического включения и выключения.
// Яркость светодиода
/* объявляем переменную и сразу инициализируем
начальным значением, равным 0
в переменной храним текущее значение яркости */
int brightness = 0;
void setup()
{
// пин светодиода 9 настраиваем на выход
pinMode(9, OUTPUT);
// режим INPUT для пина кнопки не указываем
// по умолчанию пины настроены на этот режим
}
void loop()
{
// если кнопка нажата ...
if (digitalRead(8) == HIGH) {
// увеличиваем текущую яркость на 15
brightness = brightness + 15;
// небольшая задержка, чтобы убрать
// ложное повторное срабатывание
delay(100);
}
// подаём ШИМ-сигнал на пин светодиода
analogWrite(9, brightness);
}Чтобы подать ШИМ-сигнал на выбранный пин используется функция analogWrite(пин, заполнение). Коэффициент заполнения может находится в диапазоне от 0 (всегда выключен) до 255 (всегда включён).
Добавьте к схеме ещё одну кнопку, подключённую к пину 7. При нажатии на кнопку значение яркости должно уменьшаться на 15.
Составьте программу, которая будет управлять вращение сервомотора. Последовательно поверните на 45, 90 и 180 градусов. Затем верните в начальное положение - 0 градусов.
// Управление серводвигателем
// подключение библиотеки Servo
#include <Servo.h>
// управляющий пин серводвигателя
#define SERVO_PIN 9
/*
создаём объект класса Servo
с помощью этого объекта сможем
управлять двигателем
*/
Servo motor;
void setup()
{
// с помощью метода attach() указываем
// номер управляющиего пина серводвигателя
motor.attach(SERVO_PIN);
}
void loop(){
motor.write(45); // поворачиваем в положение 45 градусов
delay(1000);
motor.write(90); // поворачиваем в положение 90 градусов
delay(1000);
motor.write(180); // поворачиваем в положение 180 градусов
delay(1000);
motor.write(0); // поворачиваем в положение 0 градусов
delay(2000);
}Чтобы управлять сервоприводом используем готовую библиотеку Servo. Чтобы импортировать библиотеки используется директива #include.
Соберите схему с сервомотором и потенциометр. Вращение ручки потенциометра должно управлять положением сервомотора. Получится своеобразный пантограф.
Схема этого задания основана на предыдущем. Можете добавить потенциометр к уже собранной схеме.
Напряжение на среднем контакте потенциометра зависит от положения ручки управления. По этому средний контакт подключен к аналоговому входу платы.
// Пантограф
// импортируем библиотеку Servo
#include <Servo.h>
#define SERVO_PIN 9 // управляющий пин сервомотора
#define POT_PIN A0 // аналоговый пин для потенциометра
int angle; // положение сервомотора (угол) 0..180
int pot; // значение с потенциометра 0..1023
Servo motor;
void setup()
{
// Аналоговые пины по умолчанию настроены на
// вход. Поэтому не нужно писать команду
// pinMode(POT_PIN, INPUT);
motor.attach(SERVO_PIN);
}
void loop()
{
// Получаем значение с потенциомера 0..1023
pot = analogRead(POT_PIN);
// Преобразуем значение с потенциометра
// в угол для сервомотора
angle = map(pot, 0, 1023, 0, 180);
// поворачиваем сервомотор на вычисленный угол
motor.write(angle);
}Разработайте систему правления умным светильником. Светодиод должен включаться, если освещённость в комнате падает ниже определённого порога. Порог срабатывания задаётся потенциометром.
Соберите схему, состоящую из трёх контуров:
После сборки схемы составьте программу по следующему алгоритму: