Помогите с чтение с SD карт в двумерный массив
- Войдите на сайт для отправки комментариев
Сейчас работает так: отправляю в км порт последовательность, в которой есть команда соответствиющая установке будильника, время включения будильника и на какой день недели он устанавливается. После изменения данных в массиве записываю этот массив на карту памяти. Ниобходимо читать из карты настройки и записывать в массив при старте ардуины
В общем это настройки будильника:
byte alarm[7][8] = {{23, 28, 1, 2, 23, 29, 3, 4}, {0, 29, 12, 13, 14, 15, 16, 17}, {20, 21, 22, 23, 24, 25, 26, 27}, {30, 31, 32, 33, 34, 35, 36, 37}, {15, 33, 42, 41, 15, 34, 46, 47}, {50, 51, 52, 53, 54, 55, 56, 57}, {60, 61, 62, 63, 64, 65, 66, 67}};
Вот так пишу на SD карточку, покрайней мере для первой строки, т.е. понедельника:
SD.remove("Setup/alarm.txt"); myFile = SD.open("Setup/alarm.txt", FILE_WRITE); if (myFile) { myFile.print(alarm[0][0]); myFile.print(";"); myFile.print(alarm[0][1]); myFile.print(";"); myFile.print(alarm[0][2]); myFile.print(";"); myFile.print(alarm[0][3]); myFile.print(";"); myFile.print(alarm[0][4]); myFile.print(";"); myFile.print(alarm[0][5]); myFile.print(";"); myFile.print(alarm[0][6]); myFile.print(";"); myFile.println(alarm[0][7]); myFile.close(); Serial.println("done."); }
В файле получаю вот такую строку:
23;22;1;2;23;29;3;4
Вот так читаю его в ком порт:
myFile = SD.open("Setup/alarm.txt"); if (myFile) { Serial.println("alarm.txt:"); myFile.close(); }
Умнейшие, помогите пожалуйста! Как стоит записывать в файл и как читать? была здесь подобная тема, с чтением в массив, но сейчас не смог найти(
Как разделять часы и минуты разницы нет, главное чтоб работало. Думал, сделать как вариант дописывания нуля перед теми значениями которые меньще 10, но получится сильно грамоздко, записывать за 7*8 тактов, как то не то, учитывая что это будит далеко не единственный массив. или просто подскажите как сделать расделение прочитанового из файла чтобы можно было записать в массив.
Ну, а дальше (читать) уж очень сильно зависит от того что в каком виде нужно отправить это в serial. Кто там читать будет прога или человек? Если прога, то возможно и сохранять нужно чуть-чуть по другому (что-бы не танцевать с конвертациями int<->string), если нужно "отправить копию файла в serial" - чуть-чуть по другому. Просто вычитывам по байту с помощью http://arduino.cc/en/Reference/FileRead и отправляем в serial
leshak, я вас понял, но мне нужно впихнуть чтение в void setup(), чтобы читалось только при запуске, а писалось только при изменении, насколько я понял for будит писать по одной ячейке за один проход цикла, а для void setup() это не подходит, может и ошибаюсь, просто вообще програмирую только с начала этого января.
Сейчас меня больше итерисует как писать в двумерный массив alarm[][] получая инфу с SD карты, точнее как ее делить по ячейкам. Можно дописывать ноль перед теми значениями которые меньше 10 при записи на SD карту, и там по накатанному. А каким способом писать на карточку как то всеравно.
В общем подскажите как оптимальнее вписать за один такт в массив с SD карты!
>leshak, я вас понял, но мне нужно впихнуть чтение в void setup()
Значит не поняли. Мой первый пример - практически полный аналог ваших строчек с 5 по 12-тую включительно. Разница только в том, что у вас в итоге после последнего элемента в строке не будет точки с запятой, а у меня будет.
Если же перед myFile.print(";"); написать if(j!=7) то получится полный аналог.
И без раницы где этот код использовать. В loop или setup. Между ними разница только в том, что setup вызывается один раз при старте контроллера, а loop - бесконечно. Поэтому то что вам нужно "выполнить" один раз - помещаете в setup, а что "много раз" - в loop. Но "что именно выполнить" - ограниченно только вашей фантазией.
Если задача "прочитать в массив", то я бы пожертвовал "читаемостью файла для человека" (если его открыть в Блокноте там будут кракозяблы), но зато упростился-бы код. Нужно, всего-лишь, писать в файл не с помощью print, а с помощью write и выкинуть все точки с запятой и переводы строк - они нужны для человека.
Итак пишем массив:
Читаем массив
>записывать за 7*8 тактов
Если вы хотите оперировать "тактами", то вам нужна не ардуина а голый камень и ассемблер. И то 7*8 тактов у вас не получится. Далеко не каждая команда выполняется за один такт. Только самые базовые. Причем под "командой" подразумевается машинная команда, а не "строка кода на C++". А уж работа с SD картой это вообще тысячи тактов. Не говоря про то что сама операция записи, по меркам контроллера это жутко медленно ее ждать ее завершения это почти века. Когда вы пишете file.Open - поверьте это далеко не один такт :)
Вернемся к нашим баранам. Данные примеры примеры предполагают, что читается и пишется массив одинаковой длины. То есть если записали 7x8, то и читать нужно 7x8, а не 8x7 или 9x8.
В данных примерах и записывается и читается "по одному байту". Запись можно немного "ускорить", если вы посмотрите на http://arduino.cc/en/Reference/FileWrite то у нее есть вариант вызова, когда передается не "один байт", а буффер (массив) и его длина. А вот вычитывать, все равно прийдется "под одному".
Ну про for, я таки понял сразу, сам использую такой метод для сравнения тоже двумерных масивов и выводил двумерный массив в serial.ptint, когда искал ошибки в коде.
С myfile.write буду сейчас пробовать. К тому же, вы скорее правы, ведь все же использую библиотеки... просто мой выбор скорее с практичной стороны уменьшения вычеслений и просто тыкать в нужное положение данных, а не вычислять их положения, чем со стороны объема кода, пока мой код занимает 24 Кб из 256 у меги. Мне так казалось правильным с точки зрения скорости, покрайней мере с этим я столкнулся при случае когда изменяется яркость 12 светодиодов и в промежутке извенения поступает другая команда, на выполнение других действий, например снятие температуры с DS18B20. К тому же, каждый раз будит открываться запись на SD карту и закрываться, пока не пройдет обв for. В моем случае, вроди как за один заход открытия/закрытия. В общем пока добьюсь нормальной работы, а потом буду думать над упрощением жизни, если это будит необходимо.
Кстати, я так понял с SD картами вы работали, тут ситуация такая, на FAT32 скорось записи и чтения гораздо медленее чем на FAT16. Столкнулся с этим когда запустил цикл на постоянную запись и чтение, и в serial monitor-е скорость таки кординально отличима на глаз!
Есть еще вопрос. Как правильно будит выйти из цыула for, примерно так:
Просто если не выйти из цикло то у меня все уйдет в бесконечный цикл, а поменять условие входа в цикл я не могу, т.к. слишком много переделывать, и это будут скорее костыли..
>В моем случае, вроди как за один заход открытия/закрытия.
Так и в моем же случае тоже самое. Перез циклом открыли, после - закрыли. Еще раз, это же аналог вашего кода. Просто написанный без использования copy-paste.
>Кстати, я так понял с SD картами вы работали
Неа. Просто почитал документацию. На этом форуме 80% ответов можно дать просто "заглянув в доки", еще 10% нужно погуглить, 5% подумать и только осташиеся пять "нужно копаться или знать ответ заранее".
Ну то есть с файлами-то я работал, только на большом компе. Но ведь "алгоритмы"/логика не отличаются. Те же объекты, те же методы. Открыть/закрыть. Записать. Просто смотришь в доку и подбираешь какие функции лучше сделаю что-ты хочешь. Из тех что доступны. В данном случае, по крайней мере пока, я не вижу ничего "ардуино" или SD-картно специфического. Работа с массивами и потоками.
> В общем пока добьюсь нормальной работы, а потом буду думать над упрощением жизни, если это будит необходимо.
А вот это очень здраво. "Преждевременная оптимизация" это один из грехов :) Правда нужно старатся код писать так, что-бы потом его было не трудно менять, в том числе и оптимизировать. И вот в этом случае "копипас-ты" ни к чему хорошему не приводят. Вот в моем примере, нужно было поменять print на write - сменил в одном месте. А у вам пришлось-бы в 8 ми местах менять. А если бы вы так сохранили полностью массив, то в 56-ти. Я конечно понимаю что есть "глобальная замена", но если поменять нужно что-то сложней чем "слово" на "слово" - она не поможет.
По хорошему, вообщем эти записи/чтения нужно вынести в отдельные функции типа saveArray(fileName) и loadArray(fileName). Тогда вы сможете менять их логику, оптимизировать, подкручивать форматы записи не затрагивая остальной скетч.
>Есть еще вопрос. Как правильно будит выйти из цыула for
http://arduino.ru/Reference/Break и arduino.ru/Reference/Continue в зависимости от того что вам нужно.
ну и посмотрите еще http://arduino.ru/Reference/While и arduino.ru/Reference/DoWhile . Возможно они больше подойдут в вашей ситуации чем for. Раз возник такой вопрос.
>примерно так:
да примерно так. Ну разве что break, так как он один оператор который нужно выполнить при j==7 можно было не оборачивать в {} (но это не ошибка).
А вот myfile.write и if(j==7) уже нужно было "совместно обернуть". Это уже ошибка. Я не "оборачивал" write потому что он единственным оператором в теле цикла. Раз вы захотели туда еще что-то добавить, то нужно "обернуть". А то у вас получилось. Отдельно цикл, отдельно if(j==7)break; То есть вы попытались выйти из цикла не находясь в нем. В этом случае break; скоре всего будет интерпретирован как "выйти из текущей функции", а не цикла. Правда и до такой катастрофы-бы дело не дошло. Так как j локальная переменная внутри цикла, а if(j==7) у вас получился "вне цикла", то компилятор вам бы сразу сказал "а не знаю я такой переменной j" на строчке if(j==7)
Вообщем читайте arduino.ru/Reference/Braces
Ну и смысл делать break; когда после него ничего нет, да и условие break совпадает с условием выхода из цикла - от меня ускользает. Разве что из подхода "кашу маслом не испортишь".
>Просто если не выйти из цикло то у меня все уйдет в бесконечный цикл
Это я тоже не понимаю. Но, видимо, вы говорите про какой-то другой свой код.
Кстати break; это "синтаксически сахар" (просто что-бы компкатней писать), вполне можно обходится и без него. Только if-ми или химича с переменными входящими в условие for-a (но эту "химию" стоит делать только в образователь целях, что-бы лучше понять for , в реальном коде за такое руки отбивают, так как поведение кода становится "очень не очевидно").
А вообще я бы советовал отложить, пока, реальную задачу и поигратся разобратся с управляющими операторами, синтаксисом, типами. Писать поэму не ознакомившись с буквами - немного странно. Хотя-бы прочитайте бегло "что имеется в наличии". А потом уже, когда оно потребуется вспомните что "где-то было такое" и "где нужно почитать/углубится".
Почитайте книжечки по C++, JAVA, C# на большом компе. Почитайте книжки типа "Идеальный код", Рефакторинг и т.п. Задачей выработки "правильного стиля" лучше начать заниматся как можно раньше ( эх.... а ведь мне тоже говорили :), не слушал, потом намного тяжелей).
А то получите такой код, который от которого самого будет тошнить. И "поменять не могу, бо слишком долго переделывать" - первые звоночки этого. Того что вы уже "не можете нормально управлять кодом". Причем кода у вас сейчас - максимум несколько сотен строк.
У меня например, в текущем домашем хобби-проекте-выходного-дня (на компе, не ардуино) 5476 строк кода. И я спокойно могу поменять любую часть и любую логику. Без страха что "все остальное навернется". И это вообщем-то "очень маленький проект", который только начался. (вообщем "сам себя не похвалишь - сидишь как оплеванный" ;) . Потому что старатся писать код который "легко поддерживать", в реальной жизни намного важней чем "пытаться выжать лишний такт на каждой операции". В микроконтроллерах это правило конечно немного "отступает", но все равно старатся нужно.
Написал вот так:
Все получается нормально, вроди относительно быстро. Дописывание нуля неред цифрами меньше 10, необходимы чтобы потом проще было писать в массив, и не смешать данные.
Ну если цель сделать, все-таки "человекочитаемый" .txt файл, то да все выглядит здорово. И решение с "пишем 0 что-бы потом было легче читать" - тоже здравое. Действительно логику чтения это существенно упростит.
Я бы, раз, все-таки, делаем для человека, то добавил-бы разделитель ";" между числами, а при чтении просто игнорировал каждый третий байт. И перевод строки.
Ну и, чисто для красоты, строчки 08-11 написал-бы "в одну строчку" и убрал-бы как необязательные фигурные скобки (строки 09 и 11).
Правда не забудте, при чтении. Что раз вы писали в файл print-том, то в файле у вас будет не значение записанного байта, а код его символа. И read прочитает этот код.
То есть если вы сделаете, например, myFile.print(3), а потом прочитаете его byte bt=myFile.read(); , то в bt будет не 3, как возможно вы ожидаете, а 51
:) что-бы в нем было "то шо нужно", нужно вычитывать так byte bt=myFile.read()-'0';
>А вот это очень здраво. "Преждевременная оптимизация" это один из грехов :) Правда нужно старатся код писать так, что-бы потом его было не трудно менять, в том числе и оптимизировать. И вот в этом случае "копипас-ты" ни к чему хорошему не приводят. Вот в моем примере, нужно было поменять print на write - сменил в одном месте. А у вам пришлось-бы в 8 ми местах менять. А если бы вы так сохранили полностью массив, то в 56-ти. Я конечно понимаю что есть "глобальная замена", но если поменять нужно что-то сложней чем "слово" на "слово" - она не поможет.
Когда начал учился, и не использовал еще массивы, то мне помогало копирование участка кода в блокнот, и замена в этом куске)
>Это я тоже не понимаю. Но, видимо, вы говорите про какой-то другой свой код.
Таки свой, правильно, вот:
Что читать нужно, я согласен и читаю, вообще изначально думал, что меня ткнут в подобную задачу, 2 месяца назад ее сдесь видел, но сейчас найти не смог(
Почитав книжки, и ничего не поняв, да и на некоторых своих примерах понял что мыслю я совсем не как програмист и мне необходимо наглядное проявление кода, иногда почти каждую строчку принтил, да и програмированием до этого вообще не занимался, для себя решил делать так:
1. Учусь на примерах из книжки, кстати очень помогли видео уроки джереми блума с переводом.
2. Дальшес играюсь с примерами в доль и поперек.
3. А вот сейчас собираю потихоньку в кучу, допиливая то что не мог сделать по отдельности
Да и еще, в байт-то помещаются значения от нуля до 255. Поэтому если нет 100% уверенности что в массиве никогда не будет лежать ничего >99, то при <10 нужно дописывать 00, а при if(arg[i][j]<100 && arg[i][j]>9) один ноль.
Если использовать:
то ничего не выходит, с вот такой ошидкой:
sketch_feb26b.cpp: In function 'void loop()':
sketch_feb26b:276: error: 'myfile' was not declared in this scope
а с print все без запинок
>Когда начал учился, и не использовал еще массивы, то мне помогало копирование участка кода в блокнот,
Программист должен быть ленив. Если какая-то работа "рутинна", то нужно найти способ возложить ее на компьютер :)
>Таки свой, правильно, вот:
Ну тут потребности в break; - я не вижу. И шансов "войти в бесконечный цикл" - тоже. Разве что вы где-то внутри цикла начнете менять i,j но это как раз и будет "за что нужно бить по рукам".
>мыслю я совсем не как програмист
Наоборот. В точности. "Познание через примеры" и "пощупать" это, IMHO вообще единственный способ научится программить. Лекции и "только чтение" точно ничего не дадут. Просто по вашим вопросам возникло впечатление что вы чтением "вообще пренебрегаете", но видимо это ошибочное впечатление.
А финт с "дописыванием нуля" это вообще типично программерский ход мышления. Может вам он и кажется "очевидным", но поверте, для среднего человека это "мозговой выкрутас" который в голову не пришел бы.
> для себя решил делать так
Ни по одному пункту нельзя возразить. Все-бы так подходили к обучению программинга :)
>то ничего не выходит, с вот такой ошидкой:
Ну само-собой. Я же дал "кусочек кода", а не скетч целиком. Файл-то открыть надо и закрыть тоже нужно. Это кусок записи должен был заменить строки 05-12 из вашего первого сообщения, во втором кусоке кода. А все что "вне их" и так "подразумевалось". Я же упоминал что это "аналог ваших строк с myFile.print", а не "аналог всего вашего примера". Я только запись и показал. Открытие/закрытие файла вы же, вроде уже сами сделали.
Да и тут, раз компилятор сказал что 'myfile' was not declared (переменная myfile не определена), то логично предположить что ее нужно объявить где-то раньше (ну и потом закрыть).
Да и в #8 вы показали вполне-себе рабочий пример. Так что я удивлен вашим вопросом. Возможно уже таки "пора спать". Обоим ;)
Кстати вы же, когда давали пример в #8 тоже "где-то за кадром" оставили объявление массива inserial, да и сами setup(), loop() не видны :)
Кстати вспомнил почему хотел сделать запись в файл за один цикл прохода всего кода.
После прихода команды на изменение настройки будильника запустит if в сторке 01, и сразу же прийдет другая команда которая не входит в условие if в строке 01, то на карточку у меня не допишется весь массив!
>Ну само-собой. Я же дал "кусочек кода", а не скетч целиком. Файл-то открыть надо и закрыть тоже нужно. Это кусок записи должен был заменить строки 05-12 из вашего первого сообщения, во втором кусоке кода. А все что "вне их" и так "подразумевалось". Я же упоминал что это "аналог ваших строк с myFile.print", а не "аналог всего вашего примера". Я только запись и показал. Открытие/закрытие файла вы же, вроде уже сами сделали.
само сабой так и сделал, просто заменил ptint на write, и другие комбинации пробывал, нихочет и все, может библиотека такая, просто я уже боюсь их менять, т.к. несколько дней убил на поиски совместимых в одной куче для ds1307, 1-wire, SD, Wire(I2C) под Arduino IDE 1.0 и еще нужно прикрутить Ethernet
>Да и тут, раз компилятор сказал что 'myfile' was not declared (переменная myfile не определена), то логично предположить что ее нужно объявить где-то раньше (ну и потом закрыть).
таки объявлена как глобальная, почему и не понял что происходит, просто забил
Да и в #8 вы показали вполне-себе рабочий пример. Так что я удивлен вашим вопросом. Возможно уже таки "пора спать". Обоим ;)
Кстати вы же, когда давали пример в #8 тоже "где-то за кадром" оставили объявление массива inserial, да и сами setup(), loop() не видны :)
За кадром только для глобальных переменных уже получилось 125 строк), полностью уже перевалило за 500, и по задумке это еще очень далеко до конца)
Ну, во первых, написание логики в наждне на то "одна команда УСПЕЕТ выполнится раньше чем прийдет следующая" - вкорне неверна.
Какую бы быструю реализацию вы не написали, всегда есть шанс что следующая прийдет еще быстрее. Поэтому нужно организовывать отдельную "очередь команд" и одна логика "ложит команды в эту очередь", а другая их "по одной выполняет". Тогда последовательный порядок их выполнение будет гарантирован.
Во вторых: откуда у вас, в микроконтроллере появится вторая команда? Это же не PC, тут многопоточности нет. Весь код выполняется последовательно. Пока вы не отработаете этот if о существовании "новой команды" вы просто не узнаете.
Псевдо-многопоточность, может появится только при использовании прерываний. Но, если вы собираетесь в функцию обработки прерывания всадить всю эту логику "записи", то "ох-хо-хо". У вас скетч будут работать "от левой пятки и фазы луны". Стабильности вы не сможете добится. Но работа с прерываниями - это, уж извините, не сегодня.
Общий подход, вратце, таков: в функции обработчике - минимальное количество кода. только регистрация самого факта "прерывание прозошло". Взведение какого-нибудь "флага-переменной", фиксация времени этого события - и все. Нужно выйти из обработчика "как можно скорее". У уж в loop() мы спокойно и не спеша, крутясь посматриваем на эти "флаги" и если кто-то из них оказался "взведенным" мы выполняем "длинную операцию" (сохранение, отсылка в serial и т.п.)
>может библиотека такая
это вряд ли. Если бы "библиотека не такая", то оно бы без этих циклов не компилировалось.
>просто я уже боюсь их менять
Для этого существуют системы "контроля версий". типа git, svn, mercurial . Помогают следить за "изменениями в исходниках", дают возможность "откатится на любой предыдущие состояние кода (можно менять что угодно безбоязненно) и т.п. И еще много чего помогающего в "управлении исходниками".
Но это на будущие. Сейчас, боюсь, разбирательство с ними вообще "взорвет мозг".
>таки объявлена как глобальная, почему и не понял что происходит
спать :) чудес не бывает. либо объявлена "не глобально", либо где-то скобку потеряли, либо еще что-то.
Но вообще, использование глобальных переменных - не самый лучший стиль программирования. Хотя, в ардуино, довольно распространненый. Просто потому что скетчи, как правило, очень небольшие и негатив от подобного "стиля" не успевает проявить себя в полную силу.