Проблема с записью на карточку SD
- Войдите на сайт для отправки комментариев
Пнд, 13/09/2021 - 17:56
Нужно запустить устройство логирования. Суть его следующая с трансформатора тока приходит сигнал (до 4В) на АЦП ADS1015. АЦП измеряеь напряжение (выходной сигнал датчика) и передает на плату Ардуино УНО. Переодичность запись 0,02 сек , но так как для УНо это практически не подьемная задача, то пока запись делается с переодичностью 2 секунды. Так как так часто дергать карточку sd вредно , то запись делается через массив (буфер) по сто элементов. Файлы просили делать с количеством 30 000 строк в формате txt или csv. Карточка micro sd, модль SD карточки стандартный, коих много в магазинах. Код программы выглядит вот так
#include <SD.h> #include <Wire.h> #include <SPI.h>// #include <DS3231.h> //внешние часы #include <Adafruit_ADS1X15.h> // Библиотека для работы с модулями АЦП ADS1115 и ADS1015 Adafruit_ADS1015 adc; // переменная АЦП #define chipSelect 4 //пин подключения СД карточки File dataFile; //переменная работы с файлами int counter = 0 ; // замена i int step_counter; uint32_t timer = 0; // переменная таймера int period; void setup() { SD.begin(chipSelect); //запуск сд карточки adc.begin();//запуск ацп adc.setGain(GAIN_TWOTHIRDS); // 2/3x gain +/- 6.144V 1 bit = 3mV (default), режим работы АЦП } void loop() { int Time [100];//массив записи времени измерения int Buffer[100];//массив который хранит значения с ацп char filename[] = "L000.txt"; // Первоначальное название первого файла for ( int a = 0; a < 1000; a++) { filename[1] = a / 100 + '0'; filename[2] = (a / 10)%10 + '0'; filename[3] = a %10 + '0'; if (! SD.exists(filename))// Проверяем наличие файла с таким именем, //если такого файла нет на карточке запускаем процес логирования в файл с этим именем { for (int step_counter =0;step_counter<300;step_counter++ )//запись буфера в файл будет производится 300 раз { period = millis() - timer;// наш флаг переодичности опроса АЦП if (period >=2000) { Buffer[counter]=(int)get_ADC();//явное преобразование float в int Time[counter]=period; timer+= 2000; counter++; //period = millis(); } if (counter ==100)//если массив заполнен то шачинаем писать в файл { byte i=0; dataFile = SD.open(filename, FILE_WRITE);//создаем файл с именем файлнейм if (dataFile) { dataFile.println(dateString());//записываем время с внешних часов для сверки while (i < counter) { dataFile.print(i+1);//порядковый номер в массиве (для проверки) dataFile.print("|"); dataFile.print( Buffer[i]); dataFile.print("|"); dataFile.println(Time[i]); ++i; } // dataFile.flush(); } dataFile.close(); counter=0; } } } } } String dateString ()//функция получения времени с внешних часов, пи открытии файла { // // Получение показаний с DS3231 DS3231 clock; // Связываем объект clock с библиотекой DS3231 clock.begin(); // Инициализируем модуль RTC DS3231 RTCDateTime DateTime; // Определяем переменную DateTime, как описанную структурой RTCDateTime (структура описана в библиотеке DS3231.h) DateTime = clock.getDateTime(); // Заполняем DateTime значениями, полученными при запросе текущего времени // String dataString = String(DateTime.day) + "." + String(DateTime.month) + "." + String(DateTime.year) + " " + String(DateTime.hour) + ":" + String(DateTime.minute) + ":" + String(DateTime.second) ; // Помещаем дату в новую строку логов Serial.println(dataString); // Выводим строку логов на монитор серийного порта return (dataString); // Возвращаем полученные значения в место вызова функции } float get_ADC()//получение значений с ацп { int16_t adc0; float volts0; adc0 = adc.readADC_SingleEnded(0); volts0 = adc.computeVolts(adc0)*150.0;//умноаем значение с АЦП на поправочный коэффициент датчика return (volts0); }
ПО частям программа работает. Если запустить генерацию файлов в цикле. То ардуино их создает, пусть не 1000файлов но штук 300 создаст. Если писать данные в один файл log. txt , то в них опять же пишется. А вот если полностью скомпилировать код и запустить, минут на пять. то происходит вот такая запись:(создалось 4 файла, в каждом файле запись производилась только один раз (один раз выгружался массив по 100 элементов) и создавались они не по порядкуа как получится. Самая первая запись это 667 фай, потом 951 , потом 461, потом 277. Почему она так работала, проблема в карточке, в ардуино или что-то я пропустил в программе?

запись L667 началась 13.9.2021 14:58:49
L951 13.9.2021 15:5:29
l461 13.9.2021 15:2:9
L277 13.9.2021 15:8:49
Буфер уже есть в библиотеке. Не надо выдумывать дополнительную чушь.
Где Вы взяли этот код?
На ошибки в функции loop указать невозможно, т.к. она вся - одна сплошная ошибка. Она неправильно задумана и спроектирована.
Ну, вот как у Вас всё это работаtт? Ведите пальцем по коду и рfссeждайте.
Достаточно? Дальше сами будете разбираться?
А вот если полностью скомпилировать код и запустить, минут на пять. то происходит вот такая запись:(создалось 4 файла, в каждом файле запись производилась только один раз (один раз выгружался массив по 100 элементов) и создавались они не по порядкуа как получится. Самая первая запись это 667 фай, потом 951 , потом 461, потом 277. Почему она так работала, проблема в карточке, в ардуино или что-то я пропустил в программе?
как вы программу написали. так оно и работает. Посмотрите внимательнее в код - вы создаете файл, потом проверяете. не прошло ли 2 секунды? - если не прошло, этот файл бросаете, ничего в него не записав, создаете следующий... и так по кругу.
Когда, наконец, две секунды прошло - вы пишете один буфер данных в очередной файл и цикл начинается заново...
В коде логики вообще нет
Где Вы взяли этот код?
На ошибки в функции loop указать невозможно, т.к. она вся - одна сплошная ошибка. Она неправильно задумана и спроектирована.
Ну, вот как у Вас всё это работаtт? Ведите пальцем по коду и рfссeждайте.
Достаточно? Дальше сами будете разбираться?
Это моя вторая программа на ардуино... увидел раздел песочница, возможно там нужно было создать эту тему, но поздно уже.
Про пункт 4 вы меня натолкнули на мысль, сейчас попробую исправить. Возможно я одну скобку не поставил из-за этого полторы недели не могу найти ошибку.
Мануалов работы с буфером не нашел (или работа с UART, а не с SD карточкой), возможно плохо искал, кроме мудренных алгоритмов которые требуют много ресурсов. Может вы подскажите , где искать?
Мануалов работы с буфером не нашел (или работа с UART, а не с SD карточкой), возможно плохо искал, кроме мудренных алгоритмов которые требуют много ресурсов. Может вы подскажите , где искать?
В учебнике по С
Про пункт 4 вы меня натолкнули на мысль, сейчас попробую исправить. Возможно я одну скобку не поставил из-за этого полторы недели не могу найти ошибку.
да нет, одной скобкой не обойдетесь. Вам нужно кардинально менять логику программы, чтобы от жестко линейной последовательности одного цикла перейти к нескольким независимым циклам- в одном вы делаете 100 измерений, в другом - пишете на данные карту раз в 2 секунды,в третьем - меняете имя файла раз в 30 записей.
Как будет время почитаю. Пока, что я читал в 2019 Айры Пола старенькую книжку, но он писал про с++
Пока нашел ошибку, я оказывается цикл записи в буфер не закрыл корректно. В результате программа заполняла раз массив , выгружала его в файл и летела создавать другой файл, как вы и говорили. Полторы недели я эту ошибку не мог увидеть. Так что вам еще раз спасибо.
На счет перехода на несколько циклов, в 8 битных контроллерах параллельные процессы программно и аппаратно не поддерживаются. Прерывание вторым таймером в атмеле ( на сколько я понял ) возможно только для того чтобы помигать светодиодом. Семафоры тут особо не применишь ( да и знакомился я с ними в 2017 -м году). Читал на одном форуме схожую проблему, там дали ответ что полноценно это решится на 32 битных стм-ках с шиной DMA ( ардуино DUE плату или какую -то не ардуино).
Для решения вашей задачи никаких параллельных процессов не требуется. Насчет прерываний и ДМА вообще полная чушь, даже комментировать не стану - вы просто ничего не поняли.
Чтобы понять , как на контроллере запустить несколько одновременных задач без реальной многозадачности - разберите широко известный пример " блинк без делей". Этот механизм подходит не только ддя мигания диодом, любая многозадачность на подобных контроллерах делается по этому принципу. Рискну даже сказать, что не поняв приципов работы миллис, ни одну серьезную программу не написать.
Сегодня эксперементировал до 4 утра с записью в переодичностью в 20 мс,
я подумаю, если будет возможность завтра над этой задачей, напишу свои соображения
вчера (вернее сегодня)с пятого раза мой ответ отправился сюда...
Вчера изменил код (попробовал переделать задачу под первоначальные условия , когда опрашивать АЦП нужно как можно чаще, например 20мс, исправил грубую ошибку в цикле где опрашивается АЦП и производит запись в файл.
-Файлы с txt изменил на CSV
-И запись времени с внешних часов(DS3231) будет производится только один раз, при открытии файла, дальше отсчет будет производится millis().
- Так же файл открывается в одном цикле функции loop() один раз и закрывается перед завершением ее итерации.
В итоге файлы создаются в правильном порядке, промежутки между записями массивов приемлимы.
Теперь у меня два вопроса:
- в итоге одна итерации (или один шаг , плохо знаю терминологию функции loop длится в пределах 11 минут, при том в начале функции loop мы файл открываем на SD карте для записи и в конце закрываем, т.е можно сказать что перезапись карточки происходит раз в 11 минут и она сможет работать так месяц по 8 часов в день?
- и выпала странная ошибка для меня, если судить по показаниям внешних часов , то генерация между файла логов длится около 11 минут, когда в эксель сумирую показаия с таймеров millis() (хазиси в массив Time[] , то cумма millis меньше на 21 секунду и так во всех файлах, которые я проверял, т .е ошибка программная, какая мне пока не удалось отследить
Вот код новой программы:
Если вас не напряжет, я буду у вас как студент спрашивать наводящие вопросы.
Блинк без delay делается миллисом, можно сделать как бы два вложенных цикла один будет писать в массив данные , а второй будет писать из массива в файл отставая на несколько элементов от первого массива. Ну это пока догадки
можно сделать как бы два вложенных цикла один будет писать в массив данные , а второй будет писать из массива в файл отставая на несколько элементов от первого массива.
можно, хотя при такой постановке вопроса не ясно, почему не писать данные сразу в файл