Usefull 3rd party libraries
Some 3rd party libraries exists, that uses timers:
- Ken Shirrifs IR library using Timer2 – Send and receive any kind of IR remote signals
- Timer1 and Timer3 library using Timer1 or tiner3 – easy way to write your own timer interrupt service routines.
Examples
Blinking LED with compare match interrupt
The first example uses the Timer1 in CTC mode and the compare match interrupt to toggle a LED. The timer is configured for a frequency of 2Hz. The LED is toggled in the interrupt service routine.
/*
* timer and interrupts
* Timer1 compare match interrupt example
* more infos: https://oscarliang.com
*/
#define ledPin 13
void setup()
{
pinMode(ledPin, OUTPUT);
// initialize Timer1
noInterrupts(); // disable all interrupts
TCCR1A = 0;
TCCR1B = 0;
TCNT1 = 0;
OCR1A = 31250; // compare match register 16MHz/256/2Hz
TCCR1B |= (1 << WGM12); // CTC mode
TCCR1B |= (1 << CS12); // 256 prescaler
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
interrupts(); // enable all interrupts
}
ISR(Timer1_COMPA_vect) // timer compare interrupt service routine
{
digitalWrite(ledPin, digitalRead(ledPin) ^ 1); // toggle LED pin
}
void loop()
{
// your program here…
}
Задержки
Простейшей с точки зрения использования функцией времени является задержка: программа “зависает” внутри функции задержки и ожидает указанное время. Задержка позволяет очень удобно и наглядно организовать работу простой “однозадачной” программы, у нас есть два варианта задержек:
-
- Задержка на указанное количество миллисекунд (мс). 1 секунда = 1’000 мс.
- принимает тип данных и может приостановить выполнение на срок от 1 до 4 294 967 295 мс (~50 суток) с разрешением 1 мс.
- Работает на системном таймере, поэтому не работает внутри прерывания и при отключенных прерываниях.
-
- Задержка на указанное количество микросекунд (мкс). 1 секунда = 1’000’000 мкс.
- принимает тип данных и может приостановить выполнение на срок от 4 до 16383 мкс (да, меньше чем максимум для этого типа данных) с разрешением 4 мкс.
- Работает не на таймере, а на пропуске тактов процессора, поэтому может работать в прерывании и при отключенных прерываниях.
- Иногда не совсем корректно работает с переменными, поэтому нужно стараться использовать константы ( или просто число).
- Часто используется в библиотеках для эмуляции цифровых интерфейсов связи.
Задержки использовать очень просто:
void setup() {} void loop() { // что-то выполнить delay(500); // подождать полсекунды }
Мышление “задержками” – главная проблема новичков. Организовать работу сложной программы при помощи задержки – невозможно, поэтому дальше рассмотрим более полезные инструменты.
Функция yield()
Разработчики Arduino позаботились о том, чтобы функция не просто блокировала выполнение кода, но и позволяла выполнять другой код во время этой задержки. Данный “костыль” получил название и работает следующим образом: если объявить функцию
void yield() { // ваш код }
то расположенный внутри неё код будет выполняться во время работы любой задержки в программе! Это решение хоть и кажется нелепым, но в то же время позволяет быстро и без написания лишних костылей и таймеров реализовать пару параллельно выполняющихся задач. Это вполне соответствует идеологии Arduino – максимально простая и быстрая разработка прототипа. Рассмотрим простой пример: стандартный мигающий светодиод, но с опросом кнопки:
void setup() { pinMode(13, OUTPUT); } void loop() { digitalWrite(13, 1); delay(1000); digitalWrite(13, 0); delay(1000); } void yield() { // а тут можно опрашивать кнопку // и не пропустить нажатия из за delay! }
Данный трюк работает на AVR Arduino, на esp8266 его нет
17.6 Ограничение Использования Ресурсов
Вы можете определять ограничения использования ресурса для
процесса. Когда процесс пробует превышать ограничение, он может
терпеть неудачу, в зависимости от ограничения. Каждый процесс
первоначально наследует значения ограничений от родителя, но он
может впоследствии изменять их.
Символы в этом разделе определены в » sys/resource.h «.
Возвращаемое значение — 0 при успехе и -1 при отказе.
Единственое возможное errno условие ошибки — EFAULT.
Возвращаемое значение — 0 при успехе и -1 при отказе. Следующее
errno условие ошибки возможно:
- EPERM
-
Вы пробовали изменять максимально допустимое значение
ограничения, но Вы не имеете привилегий, чтобы сделать это.
Rlim_cur Текущее значение рассматриваемого ограничения.
Rlim_max Максимально допустимое значение рассматриваемого
ограничения. Вы не можете устанавливать текущее значение
ограничения больше чем этот максимум. Только root может изменять
максимально допустимое значение.
В getrlimit, эта структура — вывод; она получает текущие
значения. В setrlimit она определяет новые значения.
Вот список ресурсов, для которых Вы можете определять
ограничения.
Две исторических функции для установки ограничений ресурса,
ulimit и vlimit, не зарегистрированы здесь. Они объявлены в »
sys/vlimit.h » и исходят ИЗ BSD.
Функции счёта времени
Данные функции возвращают время, прошедшее с момента запуска микроконтроллера. Таких функций у нас две:
- millis() – Возвращает количество миллисекунд, прошедших с запуска. Возвращает unsigned int, от 1 до 4 294 967 295 миллисекунд (
50 суток), имеет разрешение 1 миллисекунда, после переполнения сбрасывается в 0. Работает на системном таймере Timer 0micros() – Возвращает количество микросекунд, прошедших с запуска. Возвращает unsigned int, от 4 до 4 294 967 295 микросекунд (
70 минут), имеет разрешение в 4 микросекунды, после переполнения сбрасывается в 0. Работает на системном таймере Timer 0
Вы спросите, а как время со старта МК поможет нам организовать действия по времени? Очень просто, схема вот такая:
- Выполнили действие
- Запомнили текущее время со старта МК (в отдельную переменную)
- Ищем разницу между текущим временем и запомненным
- Как только разница больше нужного нам времени “Таймера” – выполняем действие и так по кругу
Реализация такого “таймера на millis()” выглядит вот так:
Напомню, что uint32_t это второе название типа данных unsigned long, просто оно короче в записи. Почему переменная должна быть именно такого типа? Потому что функция millis() возвращает именно этот тип данных, т.е. если мы сделаем нашу переменную например типа int, то она переполнится через 32.7 секунды. Но миллис тоже ограничен числом 4 294 967 295, и при переполнении тоже сбросится в 0. Сделает он это через 4 294 967 295 / 1000 / 60 / 60 / 24 = 49.7 суток. Значит ли это, что наш таймер “сломается” через 50 суток? Нет, данная конструкция спокойно переживает переход через 0 и работает дальше, не верьте диванным экспертам, проверьте =)
Вернёмся к вопросу многозадачности: хотим выполнять одно действие два раза в секунду, второе – три, и третье – 10. Нам понадобится 3 переменные таймера и 3 конструкции с условием:
И вот так мы можем например 10 раз в секунду опросить датчик, фильтровать значения, и два раза в секунду выводить показания на дисплей. И три раза в секунду мигать лампочкой. Почему нет?
Конструкция громоздкая, но используется очень часто, у меня в крупном проекте может быть десяток таких таймеров. Поэтому для ускорения написания кода я сделал библиотечку GyverTimer.
Reason Two — Timer 0 implementation
This second reason is not really that Arduino millis is not accurate (it
becomes accurate) but more about the implementation of the timer which is
clever but will cause a small jitter. The interrupt for the millisecond timer
(using Timer 0 in wiring.c) uses prescalers to divide down the main clock 16MHz
but the output of the timer is off by a small amount (you can not divide down a
16MHz clock using divide by 2 divider hardware to get an exact millisecond
output — -the closest you can get is 1024us). When the error gets bug enough a
correction factor is used to adjust the timer value.
So in the long term the millis() timer is accurate but in the short term it
could be out by a small amount.
This is the result you’ll get:
View plus 1 signed dec :1 signed hex :1 unsigned dec :1 unsigned hex :1 View minus 1 signed dec :-1 signed hex :FFFFFFFF unsigned dec :4294967295 unsigned hex :FFFFFFFF if millis() reaches unsigned value 0xffffffff-1 Observe the signed value signed dec :-2 signed hex :FFFFFFFE unsigned dec :4294967294 unsigned hex :FFFFFFFE View signed value of 0x7fffffff - no problem signed dec :2147483647 signed hex :7FFFFFFF unsigned dec :2147483647 unsigned hex :7FFFFFFF View signed value of 0x80000000 (The sign bit) signed dec :-2147483648 signed hex :80000000 unsigned dec :2147483648 unsigned hex :80000000
You can see that the sign bit is very important (the left most bit) and if
you use signed types you will get negative output numbers displayed, even
though the unsigned version is correct i.e. it has the expected bit value — or
hex value shown.
Also shown is the flip over point where using signed long is OK until you
reach 2147483647 (0x7fffffff) add one to that and you get -2147483648
(0x80000000). In terms of days a timer will appear to work fine for ~ and then adding one results in a negative output.
The explanation of number of days that millis() covers is .
The easy way round that is to use unsigned long (uint32_t) when dealing with
millis().
Лекции Олега Артамонова
В частности, на протяжении всех лекций Олегом делаются безапелляционные заявления о непригодности Arduino для построения сложных многозадачных систем, что просто противоречит истине и реальной практике.
На Arduino можно делать потрясающие многозадачные системы в которых в (псевдо, естественно) многозадачном режиме одновременно работают десятки и сотни сущностей (светодиодов, датчиков, актуаторов, сервоприводов, шаговых моторов, беспроводных и проводных интерфейсов и т. д.).
Топология распределённого nRF24 контроллера с огромном числом подключённого и работающего в реальном времени оборудования. Пользователь имеет дело только с «базой», работа nRF24 партнёра полностью прозрачна для него. И, да, это Arduino.
На «базе»:
— 7 сервоприводов
— 9 шаговых моторов
— 6 реле
— 3 датчика влажности почвы
— 2 датчика освещённости
— Датчик уровня воды
— Датчик влажности и температуры воздуха
На nRF24 удалённой части:
— 12 датчиков влажности почвы
— 12 реле
— 3 шаговых мотора
— 2 датчика освещённости
— Датчик уровня воды
Кроме этого, в реальном времени функционирует собственно сама nRF24 связь между двумя распределёнными частями системы и Ethernet интерфейс сервера и серверный движок, обеспечивающий веб-интерфейс пользователя системы.
Итого, в реальном времени, в многозадачном режиме на 8-битной Меге функционирует как минимум 60 сущностей (и это не считая множества сервисов самой операционной системы AMS, с ними число сущностей приблизится к сотне). Что очевидным образом никак не согласуется с высказыванием о том, что «на Arduino невозможна настоящая многозадачность и мигать даже пятью светодиодами на ней проблематично».
map()
Преобразует число из одного диапазона в другой диапазон. Т.е. значение из fromLow попадёт в toLow, значение fromHigh попадёт в toHigh, а значения между ними пропорционально попадут в новые значения другого диапазона.
Нижнее значение диапазона может быть больше или меньше верхнего значения. Функция map() в таких случаях может работать в обратном порядке. Например.
Также допускаются отрицательные числа.
Функция использует целые числа и не генерирует дробные числа. Дробные числа усекаются до целых.
- value — число для конвертации
- fromLow — нижнее значение текущего диапазона
- fromHigh — верхнее значение текущего диапазона
- toLow — нижнее значение нового диапазона
- toHigh — верхнее значение нового диапазона
Возвращается новое значение после преобразования.
Программа
Мы больше не будем использовать 13 пин и диод на плате, поэтому уберем связанные с ним переменные из кода. Но добавим массив пинов для светодиодов и счетчик.
В массиве есть элемент 13. Однако мы не будем подключать к нему диод, а будем использовать его для флага, что все светодиоды нужно выключить.
В функции setup() настроим пины на ввод и вывод.
Теперь реорганизуем код устраняющий дребезг кнопки. Мы должны передать в функцию состояние кнопки в текущий момент и получить в ответ обработанное нажатие на кнопку, если оно было.
Кроме того упростим функцию. Избавимся от подсчета миллисекунд, а просто сделаем задержку между считыванием состояния кнопки.
В основном цикле будем вызывать функцию debounce() и передавать в нее предыдущее значение кнопки. И если значения отличаются, а текущее значение = HIGH, то есть кнопка была нажата, вызываем другую функцию ledON().
Функция ledON() хранит текущее значение переменной счетчика i. И включает соответствующий светодиод из массива. Но до того, как включить нужный диод, функция выключает все. Если i = 4, это значение 13 в массиве, мы выключаем все диоды. А следующим нажатием, i = 5, сбрасываем счетчик на 0.
Работа программы
Millis() Operation
At each interrupt of 1024us the millis() timer is incremented.
Since 1024us is greater than 1000us, the millis() timer is too slow and needs correcting.
The idea is to store the error and
accumulate it until it surpasses a threshold, upon which the
millisecond timer output is corrected.
So to correct to a 1ms output, The number of interrupts before a correction is needed is:
1000.0/24 = 41.66.
The above equation represents the number of 24us periods that add up
to 1ms i.e after 41.66 interrupts the error will be 1ms. Of course you
can’t get 41.66 interrupts so you have to wait for the following
interrupt to detect when the error is greater than 1000us. This will be
when 42 interrupts have occurred.
The clever part of the algorithm is that the error accumulator is
incremented by 24 every interrupt (the division is not performed — that
just lets you see the idea). When this variable is greater than 41.66*24
i.e 42*24 = 1008 then the error is corrected.
The next really, really, clever part of the algorithm is that the
error variable is not reset to zero — you just subtract the 1ms value
(since that was what was corrected) leaving the last value of the error
in the accumulator i.e. it will be 8 on this occasion. This error then
accumulates again and the millis() timer is again adjusted when the error is greater then 1ms.
From the analysis below the millis() timer will be
continuously corrected and is not in error by more than 2ms (See simulation and real output results below).
You can explore this further by looking at the code
that follows below. One thing to note is that the values are fitted into
bytes because they are all multiples of 8 (but this only works for 16MHz and 8MHz
clocks):
1024.0 / 8 = 128.0 ; 1024 >> 3 is exactly 128 i.e. fits in a byte.
1000.0 / 8 = 125.0 ; 1000 >> 3 is exactly 125 i.e. fits in a byte.
24.0 / 8 =
3.0 ; 24
>> 3 is exactly 3 i.e. fits in a byte.
These numbers are used in the code below.
Функция delay()
Функция delay() позволяет приостановить выполнение текущей программы на указанное в параметре время. Синтаксис команды выглядит следующим образом:
//команды
delay(500); //задержка на 0,5 сек
//команды
delay(1000); //задержка на 1с
Время указывается в миллисекундах (1 сек = 1000 мс). Данный параметр может иметь тип «unsigned long», который находится в диапазоне от 0 до 4294967295. Ниже пример использования команды delay():
#define ledPin 13
void setup()
{
pinMode(ledPin,13);
}
void loop()
{
digitalWrite(ledPin,HIGH); //включить LED
delay(500); //подождать 500ms (0,5 сек)
digitalWrite(ledPin,LOW); //выключить LED
delay(1000); //подождать 1000 мс (1 сек)
}
В приведенном выше примере, светодиод загорается на 0,5 секунды, затем гаснет на 1 секунду и так далее, пока питание Arduino не будет отключено.
Примеры Millis () в Arduino IDE
Все это слова, и лучший вид функции millis () показывает несколько примеров простых эскизов IDE Arduino, чтобы вы могли увидеть некоторые приложения и варианты использования. Итак, вот некоторые практические примеры…
Может использоваться с все платы Arduino
1-Пример для объясните использование из millis ():
unsigned long inicio, fin, transcurrido; // Declarar las variables a usar void setup(){ Serial.begin(9600); //Iniciar la comunicación serial } void loop(){ inicio=millis(); //Consultar ms desde que inició la ejecución del sketch delay(1000); //Espera 1 segundo fin=millis(); //Consultar ms fin del sketch transcurrido=fin-inicio; //Calcula el tiempo desde la última lectura Serial.println(transcurrido); //Muestra el resultado en el monitor serial delay(500); //Esperar medio segundo }
Измерьте время между двумя последовательными сообщениями:
unsigned long tiempo1 = 0; //Declaramos las variables e iniciamos a 0 unsigned long tiempo2 = 0; unsigned long diferenciaTiempo = 0; void setup() { Serial.begin(9600); Serial.println("Envía la letra A/a por la terminal serial"); } void loop() { if(Serial.available() > 0){ char datoRecibido = Serial.read(); if(datoRecibido == 'A' || datoRecibido == 'a'){ tiempo1 = millis(); Serial.println("Envía la letra B/b por la terminal Serial"); } else if(datoRecibido == 'b' && datoRecibido == 'B'){ tiempo2 = millis(); diferenciaTiempo = tiempo1-tiempo2; Serial.print("El tiempo transcurrido entre el primer y último dato enviado es: "); Serial.print(diferenciaTiempo); } } }
Делать мигать светодиодом с millis ():
int estadoLed; //Almacena el estado del LED (Encendido o apagado) int periodo = 100; //Tiempo que está el LED encendido o apagado unsigned long tiempoAnterior = 0; //Almacena tiempo de referencia para comparar void setup() { pinMode(13,OUTPUT); //Configura el pin 13 como salida para el LED } void loop() { if(millis()-tiempoAnterior>=periodo){ //Evalúa si ha transcurrido el periodo programado estadoLed=!estadoLed; //Cambia el estado del LED cada 100ms digitalWrite(13,estadoLed); //Actualiza el estado del LED al actual tiempoAnterior=millis(); //Almacena el tiempo actual como referencia } }
Создать простой секвенсор для отправки текста через последовательный монитор через разные промежутки времени с помощью millis ():
#define INTERVALO_MENSAJE1 3000 #define INTERVALO_MENSAJE2 5000 #define INTERVALO_MENSAJE3 7000 #define INTERVALO_MENSAJE4 15000 unsigned long tiempo_1 = 0; unsigned long tiempo_2 = 0; unsigned long tiempo_3 = 0; unsigned long tiempo_4 = 0; void print_tiempo(unsigned long tiempo_millis); void setup() { Serial.begin(9600); } void loop() { if(millis() > tiempo_1 + INTERVALO_MENSAJE1){ tiempo_1 = millis(); print_tiempo(tiempo_1); Serial.println("Soy"); } if(millis() > tiempo_2 + INTERVALO_MENSAJE2){ tiempo_2 = millis(); print_tiempo(tiempo_2); Serial.println("Un mensaje"); } if(millis() > tiempo_3 + INTERVALO_MENSAJE3){ tiempo_3 = millis(); print_tiempo(tiempo_3); Serial.println("De"); } if(millis() > tiempo_4 + INTERVALO_MENSAJE4){ tiempo_4 = millis(); print_tiempo(tiempo_4); Serial.println("Esperanza"); } } void print_tiempo(unsigned long tiempo_millis){ Serial.print("Tiempo: "); Serial.print(tiempo_millis/1000); Serial.print("s - "); }
Вы уже знаете, что для Дополнительную информацию вы можете скачать бесплатный курс программирования Arduino в формате PDF.
Описание компонентов
В нашем проекте мы используем:
- Arduino Uno
- Troyka Shield
-
Четырёхразрядный цифровой индикатор
- Часы реального времени
- Батарейку CR1225
Часы реального времени
Мы используем модуль с часами реального времени от Seeed Studio. Они построены на базе микросхемы DS1307 от Maxim Integrated. Из элементов обвязки она требует три резистора, часовой кварц и батарейку, которые уже имеются на данном модуле. Модуль обладает следующими свойствами:
- Подсчет времени (секунды, минуты, часы), даты (год, месяц, число), дня недели
- Двухпроводной интерфейс I²C
Суть часов реального времени в том, что при наличии батарейки, они могут идти даже если основное устройство обесточено. Мы с такими часами сталкиваемся постоянно в ноутбуках или цифровых фотоаппаратах. Если достать из этих устройств аккумулятор, а через некоторое время вернуть их обратно, то время не сбросится. В этом заслуга часов реального времени, Real Time Clock (RTC).
Индикатор
Мы используем четырёхразрядный индикатор от Seeed Studio. Основное в индикаторе — микросхема TM1637, представляющая собой драйвер для отдельных 7-сегментных разрядов. В данном модуле используется 4 разряда. Модуль обладает следующими свойствами:
- 8 градаций яркости
- Двухпроводной интерфейс работы (CLK, DIO)
Данный модуль мы используем для показа времени: часов и минут. Удобство модуля в том, что подключается он всего по двум проводам и не требует программной реализации динамической индикации, поскольку все уже реализовано внутри модуля.
Динамическая индикация — это процесс, при котором индикаторы в нашем модуле загораются последовательно. Но мерцания мы не видим, поскольку человеческой глаз обладает большой инертностью. Данный метод позволяет очень хорошо экономить количество соединений между индикаторами и контроллером:
- Статическая индикация: 4 цифры × 7 сегментов = 28 соединений.
- Динамическая индикация: 7 сегментов + 4 общих анода или катода = 11 соединений.
- Микросхема TM1637: 2 соединения.
Выгода очевидна.
Функция delayMicroseconds
Данная функция является полным аналогом delay за исключением того, что единицы измерения у нее не миллисекунды, а микросекунды (в 1 секунде – 1000000 микросекунд). Максимальное значение будет 16383, что равно 16 миллисекундам. Разрешение равно 4, то есть число будет всегда кратно четырем. Кусочек примера будет выглядеть следующим образом:
Проблема с delayMicroseconds точно такая же, как у delay – эти функции полностью «вешают» программу и она на некоторое время буквально замирает. В это время невозможна работа с портами, считывание информации с датчиков и произведение математических операций. Для мигалок данный вариант подходит, но опытные пользователи не используют её для больших проектов, так как там не нужны такие сбои. Поэтому, гораздо лучше использовать функции, описанные ниже.
Откуда берётся время?
Начнём с того, откуда вообще микроконтроллер знает, сколько проходит времени. Ведь у него нет часов! Для работы микроконтроллера жизненно важен так называемый тактовый генератор, или кварцевый генератор, или он же кварц. Он же oscillator, он же clock. Clock по-английски это часы. Да, но не всё так просто =) Кварц расположен рядом с МК на плате (также во многих МК есть встроенный тактовый генератор), на Ардуинах обычно стоит кварц на 16 МГц, также встречаются модели на 8 МГц. Кварц выполняет очень простую вещь: он пинает микроконтроллер со своей тактовой частотой, то есть 16 МГц кварц пинает МК 16 миллионов раз в секунду. Микроконтроллер, в свою очередь зная частоту кварца, может прикинуть время между пинками (16 МГц = 0.0625 микросекунды), и таким образом ориентироваться во времени. Но на деле не всё так просто, потому что принимают пинки таймера так называемые таймеры-счётчики (Timer-counter). Это физически расположенные внутри МК устройства, которые занимаются подсчётом пинков тактового генератора. И вот микроконтроллер уже может обратиться к счётчику и спросить, а сколько там натикало? И счётчик ему расскажет. И вот этим мы уже можем пользоваться, для этого у Ардуино есть готовые функции времени. В Ардуино на МК 328 имеются три счётчика, и подсчётом времени занимается таймер под номером 0. Этим может заниматься любой другой счётчик, но работая в Arduino IDE вы сразу получаете такую настройку, т.к. создавая скетч в Arduino IDE вы автоматически работаете с библиотекой Arduino.h, где и реализованы все удобные функции.
Technical Notes
Internal system time is based on the standard Unix .
The value is the number of seconds since Jan 1, 1970.
System time begins at zero when the sketch starts.
The internal time can be automatically synchronized at regular intervals to an external time source.
This is enabled by calling the function — the provider argument is
the address of a function that returns the current time as a .
See the sketches in the examples directory for usage.
The default interval for re-syncing the time is 5 minutes but can be changed by calling the
method to set the number of seconds between re-sync attempts.
The Time library defines a structure for holding time elements that is a compact version of the C structure.
All the members of the Arduino structure are bytes and the year is offset from 1970.
Convenience macros provide conversion to and from the Arduino format.
Low-level functions to convert between system time and individual time elements are provided:
breakTime(time, &tm); // break time_t into elements stored in tm struct makeTime(&tm); // return time_t from elements stored in tm struct
This DS1307RTC library provides an example of how a time provider
can use the low-level functions to interface with the Time library.
Передача массива в качестве аргумента
Как уже говорилось ранее, имя массива подменяется на указатель, поэтому передача одномерного массива эквивалентна передаче указателя. Пример:
функция получает массив и его размер и выводит на печать:
#include <conio.h> #include <stdio.h> void printArray(int *arr, unsigned size) { unsigned i; for (i = 0; i < size; i++) { printf("%d ", arr); } } void main() { int x = {1, 2, 3, 4, 5}; printArray(x, 10); getch(); }
В этом примере функция может иметь следующий вид
void printArray(int arr[], unsigned size) { unsigned i; for (i = 0; i < size; i++) { printf("%d ", arr); } }
Также напомню, что правило подмены массива на указатель не рекурсивное. Это значит, что необходимо указывать размерность двумерного массива при передаче
#include <conio.h> #include <stdio.h> void printArray(int arr[], unsigned size) { unsigned i, j; for (i = 0; i < size; i++) { for (j = 0; j < 5; j++) { printf("%d ", arr); } printf("\n"); } } void main() { int x[] = { { 1, 2, 3, 4, 5}, { 6, 7, 8, 9, 10}}; printArray(x, 2); getch(); }
Либо, можно писать
#include <conio.h> #include <stdio.h> void printArray(int (*arr), unsigned size) { unsigned i, j; for (i = 0; i < size; i++) { for (j = 0; j < 5; j++) { printf("%d ", arr); } printf("\n"); } } void main() { int x[] = { { 1, 2, 3, 4, 5}, { 6, 7, 8, 9, 10}}; printArray(x, 2); getch(); }
Если двумерный массив создан динамически, то можно передавать указатель на указатель. Например функция, которая получает массив слов и возвращает массив целых,
равных длине каждого слова:
#include <conio.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #define SIZE 10 unsigned* getLengths(const char **words, unsigned size) { unsigned *lengths = NULL; unsigned i; lengths = (unsigned*) malloc(size * sizeof(unsigned)); for (i = 0; i < size; i++) { lengths = strlen(words); } return lengths; } void main() { char **words = NULL; char buffer; unsigned i; unsigned *len = NULL; words = (char**) malloc(SIZE * sizeof(char*)); for (i = 0; i < SIZE; i++) { printf("%d. ", i); scanf("%127s", buffer); words = (char*) malloc(128); strcpy(words, buffer); } len = getLengths(words, SIZE); for (i = 0; i < SIZE; i++) { printf("%d ", len); free(words); } free(words); free(len); getch(); }
Можно вместо того, чтобы возвращать указатель на массив, передавать массив, который необходимо заполнить
#include <conio.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #define SIZE 10 void getLengths(const char **words, unsigned size, unsigned *out) { unsigned i; for (i = 0; i < size; i++) { out = strlen(words); } } void main() { char **words = NULL; char buffer; unsigned i; unsigned *len = NULL; words = (char**) malloc(SIZE * sizeof(char*)); for (i = 0; i < SIZE; i++) { printf("%d. ", i); scanf("%127s", buffer); words = (char*) malloc(128); strcpy(words, buffer); } len = (unsigned*) malloc(SIZE * sizeof(unsigned)); getLengths(words, SIZE, len); for (i = 0; i < SIZE; i++) { printf("%d ", len); free(words); } free(words); free(len); getch(); }
На этом первое знакомство с функциями заканчивается: тема очень большая и разбита на несколько статей.
Q&A
Всё ещё не понятно? – пиши вопросы на ящик
Функция millis вместо delay
Функция millis() позволит выполнить задержку без delay на ардуино, тем самым обойти недостатки предыдущих способов. Максимальное значение параметра millis такое же, как и у функции delay (4294967295мс или 50 суток). При переполнении значение просто сбрасывается в 0, не забывайте об этом.
С помощью millis мы не останавливаем выполнение всего скетча, а просто указываем, сколько времени ардуино должна просто “обходить” именно тот блок кода, который мы хотим приостановить. В отличие от delay millis сама по себе ничего не останавливает. Данная команда просто возвращает нам от встроенного таймера микроконтроллера количество миллисекунд, прошедших с момента запуска. При каждом вызове loop Мы сами измеряем время, прошедшее с последнего вызова нашего кода и если разница времени меньше желаемой паузы, то игнорируем код. Как только разница станет больше нужной паузы, мы выполняем код, получаем текущее время с помощью той же millis и запоминаем его – это время будет новой точкой отсчета. В следующем цикле отсчет уже будет от новой точки и мы опять будем игнорировать код, пока новая разница millis и нашего сохраненного прежде значения не достигнет вновь желаемой паузы.
Задержка без delay с помощью millis требует большего кода, но с ее помощью можно моргать светодиодом и ставить на паузу скетч, не останавливая при этом систему.
Вот пример, наглядно иллюстрирующий работу команды:
unsigned long timing; // Переменная для хранения точки отсчета void setup() { Serial.begin(9600); }
void loop() { /* В этом месте начинается выполнение аналога delay() Вычисляем разницу между текущим моментом и ранее сохраненной точкой отсчета. Если разница больше нужного значения, то выполняем код. Если нет – ничего не делаем */ if (millis() – timing > 10000){ // Вместо 10000 подставьте нужное вам значение паузы timing = millis(); Serial.println (“10 seconds”); } }
Сначала мы вводим переменную timing, в ней будет храниться количество миллисекунд. По умолчанию значение переменной равно 0. В основной части программы проверяем условие: если количество миллисекунд с запуска микроконтроллера минус число, записанное в переменную timing больше, чем 10000, то выполняется действие по выводу сообщения в монитор порта и в переменную записывается текущее значение времени. В результате работы программы каждые 10 секунд в монитор порта будет выводиться надпись 10 seconds. Данный способ позволяет моргать светодиодом без delay.
Функция millis вместо delay
Функция millis() позволит выполнить задержку без delay на ардуино, тем самым обойти недостатки предыдущих способов. Максимальное значение параметра millis такое же, как и у функции delay (4294967295мс или 50 суток). При переполнении значение просто сбрасывается в 0, не забывайте об этом.
С помощью millis мы не останавливаем выполнение всего скетча, а просто указываем, сколько времени ардуино должна просто “обходить” именно тот блок кода, который мы хотим приостановить. В отличие от delay millis сама по себе ничего не останавливает. Данная команда просто возвращает нам от встроенного таймера микроконтроллера количество миллисекунд, прошедших с момента запуска. При каждом вызове loop Мы сами измеряем время, прошедшее с последнего вызова нашего кода и если разница времени меньше желаемой паузы, то игнорируем код. Как только разница станет больше нужной паузы, мы выполняем код, получаем текущее время с помощью той же millis и запоминаем его – это время будет новой точкой отсчета. В следующем цикле отсчет уже будет от новой точки и мы опять будем игнорировать код, пока новая разница millis и нашего сохраненного прежде значения не достигнет вновь желаемой паузы.
Вот пример, наглядно иллюстрирующий работу команды:
Сначала мы вводим переменную timing, в ней будет храниться количество миллисекунд. По умолчанию значение переменной равно 0. В основной части программы проверяем условие: если количество миллисекунд с запуска микроконтроллера минус число, записанное в переменную timing больше, чем 10000, то выполняется действие по выводу сообщения в монитор порта и в переменную записывается текущее значение времени. В результате работы программы каждые 10 секунд в монитор порта будет выводиться надпись 10 seconds. Данный способ позволяет моргать светодиодом без delay.