Для работы требуются библиотеки TimerOne, SdFat, RTClib, а также стандартные LiquidCrystal и Wire.
Код:
#define vers "version 9.38"
/*
Вариант на "Data Logger Shield" и "LCD Keypad Shield"
(на первом есть RTC -- используем для показа времени)
Выход - D3
Вход - A1
"Data Logger Shield V1.0":
SD-картридер подключен к выводам ардуино:
* MOSI - D11
* MISO - D12
* CLK - D13
* CS - D10
Часы реального времени (RTC DS1307) подключены:
* SDA - A4
* SDL - A5
"LCD Keypad Shield":
Подключение экрана 1602А:
* LCD RS pin - D8
* LCD Enable pin - D9
* LCD D4 pin - D4
* LCD D5 pin - D5
* LCD D6 pin - D6
* LCD D7 pin - D7
* LCD R/W pin - GND
* LCD 5V pin - 5V
* Кнопки - A0
*/
// Подгружаемые библиотеки:
#include <LiquidCrystal.h>
#include <SdFat.h>
#include <TimerOne.h>
#include <Wire.h>
#include "RTClib.h"
//инициализация часов
RTC_DS1307 RTC;
// инициализация экрана
LiquidCrystal lcd(8, 9, 4, 5, 6, 7);
SdFat sd;
SdFile dataFile;
char sfileName[14]; // короткое имя текущего файла/директории
int currentFile = 1; // текущая позиция в директории
int maxFile = 0; // всего позиций в лиректории (файлов и поддиректорий)
byte isDir = 0; // признак того, что текущая позиция -- это директория
boolean isRoot = true; // признак того, что мы в корне
byte BLs = 0x01; // начальный блок
unsigned int Nbt; // размер файла, байт
volatile byte BUFF[256]; // буфер данных
volatile unsigned int CRB = 0; // индекс чтения из буфера
unsigned int CRB_temp = 0; // временная переменная для CRB
volatile byte bBit = 15; // индекс читаемого полубита
volatile unsigned int CWB = 0; // индекс записи в буфер
volatile byte A = 0; // записываемый байт
volatile byte B = 0; // записываемый бит
//int i0 = 0; // счётчик циклов без сигнала
//volatile unsigned long PPeriod = 0; // текущее значение полупериода
volatile unsigned long PPeriod_sred[2]; // среднее значение границы длинны нечётного полупериода
volatile unsigned long iMicros_old = 0; // предыдущее значение микросекунд
volatile boolean Pik = false; // есть изменение сигнала
volatile byte PP = 1; // =0 -- чётный, =1 -- нечётный полупериод
// Заголовок блока
byte SB[27] = {
0x4E, 0x4F, 0x44, 0x49, 0x53, 0x43, 0x30, 0x30, // NODISK00
0x32, 0x39, 0x30, 0x33, 0x31, 0x38, // дата: 290318
0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, // имя программы
0x20, 0x20, 0x20, 0x00, 0x00 };
// строка часов
char DT[15] = {
'0', '0', ':', '0', '0', ':', '0', '0', ' ',
'0', '0', '/', '0', '0', 0x00 };
// имя файла для записи
char iFName[13] = "input000.vkt";
volatile unsigned int Tpp = 200; // Начальная длительность задержки сигнала в микросекундах (один полупериод)
volatile int Tb = 64; // Дополнительная задержка сигнала на начало байта в микросекундах
const int p = 3; // номер пина, на который будет вывод сигнала
#define InputPIN A1 // номер пина, на котором будет получение сигнала
const int BT_none = 0; // константы -- коды нажатой кнопки
const int BT_right = 1;
const int BT_up = 2;
const int BT_down = 3;
const int BT_left = 4;
const int BT_select = 5;
int MLevel = 0; // текущий пункт меню
const int M_play = 0; // воспроизведение
const int M_play_in = 10;
const int M_record = 1; // запись
const int M_record_in = 11;
const int M_setup = 2; // настройки
const int M_setup_in = 12;
void setup() {
pinMode(InputPIN, INPUT); // объявляем пин как вход (сигнал)
pinMode(p, OUTPUT); // объявляем пин как выход (сигнал)
digitalWrite(p, LOW); // выход =0
pinMode(10, OUTPUT); // CS для SD-картридера и оно же для яркости экрана
digitalWrite(10, HIGH); // включаем подсветку экрана
pinMode(A0, INPUT); // объявляем пин с кнопками как вход
lcd.begin(16, 2); // объявляем размер экрана 16 символов и 2 строки
Timer1.initialize(Tpp); // инициализировать timer1, и установить период Tpp мкс.
Timer1.attachInterrupt(SendHalfBit); // прикрепить SendHalfBit(), как обработчик прерывания по переполнению таймера
Timer1.stop(); // остановить таймер
printtext("BEKTOP-MF",0); // вывод названия
printtext(vers,1); // и версии
delay(2000); // ждем 2 с для солидности :-)
Wire.begin(); // запуск часов
RTC.begin();
if (! RTC.isrunning()) {
printtext("RTC is NOT run!",1); // часы стоят
RTC.adjust(DateTime(__DATE__, __TIME__)); // устанавливаем дату/время на момент копмиляции программы
delay(2000); // ждем 2 с
DateTime now = RTC.now(); // проверяем работу часов
if (now.month() > 12) DT[0]=0x00; // если часы не работают, обнуляем первый байт в качестве флага
}
//RTC.adjust(DateTime(__DATE__, __TIME__)); //-- это если понадобится принудительно обновить время
while (!sd.begin(10,SPI_FULL_SPEED)){ // SD-карта готова?
printtext("SD-card failed!",1);
delay(3000); // ждем 3 с
}
printtext("SD-card is OK.",1);
sd.chdir(); // устанавливаем корневую директорию SD
DIDR1 = (1<<AIN1D)|(1<<AIN0D); // Выключить цифровые входы контактов Digital 6 и 7
}
void loop() {
int RCrom = 0; // для кода возврата из ПП
int button = getPressedButton(); // какая кнопка нажата?
while(getPressedButton()!=BT_none) { // Ждём, пока не будет отпущена кнопка
delay(50);
}
switch (button) // проверяем, что было нажато
{
case BT_up: // вверх
if (MLevel < 10) {
if (MLevel > 0) MLevel--;
else MLevel = 2;
}
break;
case BT_down: // вниз
if (MLevel < 10) {
if (MLevel < 2) MLevel++;
else MLevel = 0;
}
break;
case BT_right: // вправо -- вход в меню, запуск действия и т.д.
if (MLevel < 10) {
switch (MLevel) // выводим надписи меню
{
case M_play: // воспроизведение
printtext("Play file:",0);
getMaxFile();
break;
case M_record: // запись
printtext("Record to file:",0);
sd.chdir(); // устанавливаем корневую директорию SD
while (sd.exists(iFName)) { // ищем первый отсутствующий файл по имени, увеличивая счётчик
if (iFName[7] < '9') { // увеличиваем единицы
iFName[7]++;
}
else {
iFName[7] = '0';
if (iFName[6] < '9') { // увеличиваем десятки
iFName[6]++;
}
else {
iFName[6] = '0'; // увеличиваем сотни
iFName[5]++;
}
}
}
printtext(iFName,1); // выводим имя файла для записи
break;
case M_setup: // настройки
printtext("Setup",0);
break;
}
MLevel += 10; // заходим в подменю
button = BT_none;
}
break;
case BT_left: // влево
break;
case BT_select: // Возврат в корень меню
MLevel = 0;
break;
case BT_none: // ничего не нажато
delay(100);
break;
}
switch (MLevel) // действия в соответствии с текущим пунктом меню
{
case M_play: // воспроизведение
printtext("Play file ->",0);
printtime();
break;
case M_record: // запись
printtext("Record data ->",0);
printtime();
break;
case M_setup: // настройки
printtext("Settings ->",0);
printtime();
break;
case M_play_in: // зашли в меню вопроизведения
switch (button)
{
case BT_up: // вверх по файлам
currentFile--;
if(currentFile<1) {
currentFile = maxFile;
}
seekFile(); // показать имя текущего файла/директории
break;
case BT_down: // вниз по файлам
currentFile++;
if(currentFile>maxFile) {
currentFile=1;
}
seekFile(); // показать имя текущего файла/директории
break;
case BT_left: // в корень или выход
if (isRoot) {
MLevel = M_play; // из корня выходим в стартовое меню
}
else { // возврат в корневую директорию, ремарка ниже:
//SDFat has no easy way to move up a directory, so returning to root is the easiest way.
//each directory (except the root) must have a file called ROOT (no extension)
sd.chdir(true);
isRoot = true; // помечаем, что мы в корне
getMaxFile();
}
break;
case BT_right: // вход в директорию, или запуск файла на воспроизведение
if (isDir==1) { //Если это директория, то переход в неё
sd.chdir(sfileName, true);
isRoot = false;
getMaxFile();
}
else { // если не директория -- пробуем воспроизвести файл
if (Nbt != 0xFFFF) { // проверяем размер файла
RCrom = 11; // для вывода сообщения "неверный формат"
printtext("Playing...",0);
for (int i=0;8;i++){ // цикл по имени файла
if (sfileName[i]=='.'){ // ищем точку в имени файла
if (((sfileName[i+1]|0x20)=='r')&((sfileName[i+3]|0x20)=='m')) { // проверяем первый и третий символы расширения == 'r'|'R', == 'm'|'M'
if (((sfileName[i+2]|0x20)=='o')|((sfileName[i+2]|0x20)=='0')) { // == 'o'|'O'|'0'
if ((sfileName[i+2]|0x20)!='0') { // проверка на вывод нулевого блока по расширению файла
BLs = 0x01; // с первого блока
}
else {
BLs = 0x00; // с нулевого блока
}
RCrom = PlayROM(sfileName, i);// Передаём короткое имя файла и позицию точки в качестве параметров
}
}
else { // проверяем расширение файла на "VKT"
if (((sfileName[i+1]|0x20)=='v')&((sfileName[i+2]|0x20)=='k')&((sfileName[i+3]|0x20)=='t')) {
RCrom = PlayVKT(sfileName); // вызов ПП для формата VKT
}
}
break;
}
}
}
else {
RCrom = 10; // для вывода сообщения "большой файл"
}
digitalWrite(p, LOW); // выход = 0
switch (RCrom) // Проверяем код возврата
{
case 0:
printtext("Done.",0); // Всё закончилось успешно.
break;
case 1:
printtext("Stopped",0); // Сообщение об остановке
while(getPressedButton()!=BT_none) { // Ждём, пока не будет отпущена кнопка
delay(50);
}
break;
case 10:
printtext("File is too big",0); // большой файл
break;
case 11:
printtext("Unknown format",0); // выбран не ROM/R0M/VKT-файл, либо не найдена метка скорости в VKT
break;
default:
printtext("ERROR!",0); // Сообщение об ошибке
}
delay(1000); // ждем 1 с
printtext("Play file:",0);
seekFile(); // показать имя текущего файла
}
}
break;
case M_record_in: // зашли в меню записи
if (button == BT_right) { // нажали кнопку вправо?
if (dataFile.open(iFName, FILE_WRITE)) { // открываем файл на запись
printtext("Prepare...",0); // готовимся...
dataFile.write(0xFF); // пишем байт для создания файла
dataFile.seekSet(0); // переход на начало файла
CRB = 0; // Сбрасываем индекс
printtext("Waiting...",0); // ожидание сигнала
ADCSRA = 0; // выключить АЦП
ADCSRB |= (1<<ACME); // включить мультиплексор
ADMUX = (1<<MUX0) // включить вход на A1
|(1<<REFS1)|(1<<REFS0); // опорное напряжение АЦП (?)
ACSR = (1<<ACIE) // Разрешить прерывание аналогового компаратора
|(1<<ACBG); // внутреннее опорное напряжение
delay(1); // ждём немного
Pik = false; // сбрасываем первое срабатывание
while (!Pik) { // Ждём сигнал
delay(10); // задержка...
if (digitalRead(A0)!=HIGH) { // кнопка нажата?
break; // прерывание режима записи при нажатии кнопки
}
}
printtext("Recording...",0); // сигнал зафиксирован, записываем
printtext("Bytes:",1);
delay(100); // задержка для накопления инфы...
//====================================
do { // пока есть сигнал
noInterrupts(); // запрет прерываний
CRB_temp = CWB; // сохраняем CWB во временную переменную
interrupts(); // разрешение прерываний
if (CRB_temp <= CRB) {// если индекс записи меньше или равен индексу чтения
delay(200); // задержка на ввод данных: макс.скорость 160 * 16 полубит * ~78 байт или ~31 байт на минимальной
noInterrupts(); // запрет прерываний
CRB_temp = CWB; // сохраняем CWB во временную переменную
interrupts(); // разрешение прерываний
}
dataFile.write(BUFF[lowByte(CRB)]); // пишем байт из буфера
CRB++;
if (CRB%10 == 0) {
lcd.setCursor(7, 1); // устанавливаем курсор в позицию 8 в строке 1
lcd.print(CRB); // количество сохранённых байт
}
}
while (CRB_temp >= CRB); // если запись на SD обогнала чтение, значит сигнала нет, выходим из цикла
//=====================================
ACSR = 0; // отключаем обработку прерывания компаратора
ADCSRA = 135; // включить АЦП, установить делитель =128
Tpp = ((PPeriod_sred[0]+PPeriod_sred[1])/384); // расчитываем полупериод для последующего вывода... (((S1+S2)/2)/128)*2/3
if ((Tpp%8) < 4) { // если остаток от деления на 8 меньше 4
Tpp = (Tpp/8)*8; // округляем в меньшую сторону
}
else {
Tpp = (Tpp/8+1)*8; // округляем в большую сторону
}
if (CRB > 25) { // проверка -- если было ложное срабатывание, то ничего не пишем.
dataFile.write(0xFF); // записываем маркер в файл
dataFile.write(highByte(Tpp)); // сохраняем скорость в файле
dataFile.write(lowByte(Tpp));
if (DT[0] > 0x00) { // если часы работают...
DateTime now = RTC.now(); // получаем текущее время и сохраняем в атрибутах файла
dataFile.timestamp(T_CREATE,now.year(),now.month(),now.day(),now.hour(),now.minute(),now.second());
dataFile.timestamp(T_WRITE,now.year(),now.month(),now.day(),now.hour(),now.minute(),now.second());
}
printtext("Done. Speed:",0);
lcd.setCursor(7, 1); // устанавливаем курсор в позицию 8 в строке 1
lcd.print(CRB); // количество сохранённых байт
lcd.setCursor(13, 0); // устанавливаем курсор в позицию 13 в строке 0
lcd.print(Tpp); // расчётное значение длинны полупериода для вывода
lcd.setCursor(0, 0); // устанавливаем курсор в позицию 0 в строке 0
if ((PPeriod_sred[1]>(PPeriod_sred[0]+3840))|(PPeriod_sred[0]>(PPeriod_sred[1]+3840))) { // если средние значения слишьком сильно отличаются
lcd.print("BadSig"); // пишем сообщение о плохом сигнале
// отладочный вывод инфы о полупериодах
printtext("",1);
lcd.setCursor(0, 1);
lcd.print(PPeriod_sred[0]/192);
lcd.setCursor(5, 1);
lcd.print(PPeriod_sred[1]/192);
lcd.setCursor(10, 1);
lcd.print(CRB); // количество сохранённых байт
delay(3000); // ждём 3 сек.
}
else if ((Tpp<160)|(Tpp>512)) { // если скорость вне допустимых пределов
lcd.print("Error?"); // пишем сообщение о возможной ошибке
delay(3000); // ждём 3 сек.
}
}
else {
printtext("Canceled.",0);
if (!dataFile.remove()) { // удаляем недозаписанный файл
delay(1000);
printtext("Error del.file",0);
}
}
dataFile.close(); // закрываем файл
Tpp = 200; // устанавливаем полупериод на "стандартное" значение
}
else { // что-то не открылся файл...
printtext("Error open file",0);
}
delay(1000);
MLevel = M_record; // переход в корневое меню на пункт записи
}
else {
if (button == BT_left) MLevel = M_record; // нажали кнопку влево? -- переход в корневое меню
}
break;
case M_setup_in: // зашли в меню настроек
printtext("Period(mks):",1);
switch (button)
{
case BT_up: // вверх
if (Tpp<400) Tpp += 8; // увеличиваем скорость
break;
case BT_down: // вниз
if (Tpp>160) Tpp = Tpp - 8; // уменьшаем скорость
break;
case BT_left: // влево
MLevel = M_setup; // выход в коневое меню на пункт "настройки"
}
lcd.setCursor(12,1);
lcd.print(Tpp); // пришем текущее значение скорости
break;
}
}
//=================================================================
int getPressedButton() // функция проверки нажатой кнопки
{
int buttonValue = analogRead(0);
if (buttonValue < 80) return BT_right;
else if (buttonValue < 200) return BT_up;
else if (buttonValue < 380) return BT_down;
else if (buttonValue < 600) return BT_left;
else if (buttonValue < 800) return BT_select;
return BT_none;
}
void printtext(char* text, int l) { // Вывод текста на экран в строке l с очисткой строки
lcd.setCursor(0,l);
lcd.print(text);
lcd.print(" "); // очистка строки после текста
}
void printtime() { // вывод времени и даты
lcd.setCursor(0, 1); // устанавливаем курсор в позицию 0 в строке 1
if (DT[0] > 0x00) { // если не обнулён первый байт -- часы работают
DateTime now = RTC.now(); // получаем текущее время
DT[0] = now.hour()/10 + '0'; // перевод из целого в символ
DT[1] = now.hour()%10 + '0'; // часы
DT[3] = now.minute()/10 + '0'; // минуты
DT[4] = now.minute()%10 + '0'; // минуты
DT[6] = now.second()/10 + '0'; // секунды
DT[7] = now.second()%10 + '0'; // секунды
DT[9] = now.day()/10 + '0'; // день
DT[10] = now.day()%10 + '0'; // день
DT[12] = now.month()/10 + '0'; // месяц
DT[13] = now.month()%10 + '0'; // месяц
lcd.print(DT); // выводим время и дату
lcd.print(" ");
}
else {
lcd.print(millis()/1000); // выводим количество секунд с момента влючения ардуины вместо времени
lcd.print(" ");
}
}
void getMaxFile() { // считаем файлы и директории в текущей директории и сохраняем в maxFile
dataFile.cwd()->rewind();
maxFile=0;
while(dataFile.openNext(dataFile.cwd(),O_READ)) {
dataFile.close();
maxFile++;
}
currentFile=1; // устанавливаем позицию на первый файл
seekFile(); // и переходим туда
}
void seekFile() { // переход на позицию в директории, сохранение имени файла и показ его на экране
dataFile.cwd()->rewind();
for(int i=1;i<currentFile;i++) { // читаем первые файлы до currentFile
dataFile.openNext(dataFile.cwd(),O_READ);
dataFile.close();
}
dataFile.openNext(dataFile.cwd(),O_READ); // читаем данные текущего файла
dataFile.getSFN(sfileName); // сохраняем короткое имя файла
isDir = dataFile.isDir(); // признак директории
if (dataFile.fileSize()<=0xFFFE) {// проверка размера файла <=65534 или для VKT без служебной информации будет <=44458
Nbt = dataFile.fileSize(); // размер файла ОК
}
else {
Nbt = 0xFFFF; // слишком большой для загрузки
}
dataFile.close(); // закрываем файл
if (isDir!=1) { // это не директория?
printtext(sfileName,1); // вывод имени текущего файла
}
else {
for (int i=0;i<14;i++) { // ищем конец имени файла
if (sfileName[i]==0x00) {
sfileName[i]='>'; // если это директория, добавляем в конце символ '>'
sfileName[i+1]=0x00;
printtext(sfileName,1); // вывод имени текущей директории
sfileName[i]=0x00; // и удаляем его...
break;
}
}
}
}
void CalcTb() // Вычисление значения задержки на начало байта Tb
{
if (Tpp <= 176) { // для полупериода меньше или равном 176
Tb = 88;
}
else {
if (Tpp <= 240) { // для полупериода от 184 до 240
Tb = 264 - Tpp;
}
else {
if (Tpp <= 264) { // для полупериода от 248 до 264
Tb = 16;
}
else Tb = 0; // для полупериода больше 264
}
}
}
void WaitBuffer() // ожидание очистки буфера при воспроизведении
{
do{ // Ждём опустошения буфера
delay(Tpp/64); // задержка на вывод 1 байта (~Tpp*16/1000)
noInterrupts(); // запрет прерываний
CRB_temp = CRB; // сохраняем CRB во временную переменную
interrupts(); // разрешение прерываний
}
while (CRB_temp < CWB);
Timer1.stop(); // Останавливаем таймер............
}
int PlayVKT(char FName[]) // функция вывода файла VKT
{
delay(1000); // ждем 1 с
if (dataFile.open(FName,O_READ)) { // открываем файл. Открылся?
dataFile.seekSet(Nbt-3); // на позицию -3 байт от конца файла
if (dataFile.read()!=0xFF) return 11; // метка не найдена
Tpp = dataFile.read()*256 + dataFile.read(); // считываем Tpp
if ((Tpp < 160)|(Tpp > 511)) return 11; // не правильная метка
dataFile.seekSet(0); // на начало файла
CRB = 0; // Сбрасываем индексы.
bBit = 15;
CalcTb(); // Вычисляем значение задержки на начало байта Tb
// Начинаем наполнять буфер
for (CWB=0; CWB<=250; CWB++){ // первые 250 байт
BUFF[CWB] = dataFile.read();
}
CWB = 251; // продолжаем с 251-го байта буфера
Timer1.setPeriod(Tpp); // Выставляем период таймера
Timer1.start(); // Запускаем таймер............
for (unsigned int i=251; i<=Nbt-4; i++){ // всё остальное, кроме последних 3 байт
ToBUFF(dataFile.read());
lcd.setCursor(12, 0); // выводим на экран кол-во оставшихся "псевдоблоков" по 256 байт
lcd.print((Nbt-i)>>8);
lcd.print(' ');
if (getPressedButton()!=BT_none) { // кнопка нажата?
Timer1.stop(); // Останавливаем таймер
dataFile.close(); // закрываем файл
return 1; // выход из ПП с ошибкой 1.
}
if (CRB_temp > CWB) { // проверка -- не обогнало ли чтение запись?
Timer1.stop(); // Останавливаем таймер
dataFile.close(); // закрываем файл
return 2; // выход из ПП с ошибкой 2.
}
}
dataFile.close(); // закрываем файл
}
else return 3; // нет файла
WaitBuffer(); // ожидаем окончания вывода
return 0;
}
int PlayROM(char FName[], int pnt) // функция вывода файла ROM
{
delay(1000); // ждем 1 с
if (dataFile.open(FName,O_READ)) { // открываем файл. Открылся?
byte BLe = Nbt/256; // всего блоков
byte BLt; // осталось блоков
byte Nst; // номер строки
byte St; // выводимый байт
byte CSz = 0x00; // контрольная сумма заголовка
byte CSs = 0x00; // контрольная сумма строки
byte i;
byte j;
if (Nbt%256 != 0) BLe++; // корректировка количества блоков, если размер файла не кратен 256
for (i=0; i<=7; i++){ // заносим в SB имя файла
if (i < pnt) { // имя ещё не закончилось?
if ((FName[i]>=0x61) && (FName[i]<=0x7A)) {
SB[i+14] = FName[i] - 0x20; // на заглавные буквы
}
else if (FName[i]!=0x7E) { // не тильда
SB[i+14] = FName[i];
}
else {
SB[i+14] = '_'; // меняем тильду на подчёркивание, иначе это будет русская "Ч"
}
}
else SB[i+14] = 0x20; // пробелы
}
for (i=1; i<=3; i++){ // заносим в SB расширение файла
if ((FName[pnt+i]>=0x61) && (FName[pnt+i]<=0x7A)) {
SB[i+21] = FName[pnt+i] - 0x20; // на заглавные буквы
}
else {
SB[i+21] = FName[pnt+i];
}
}
dir_t d;
dataFile.dirEntry(&d); // Считываем дату файла и сохраняем в заголовке
SB[8] = FAT_DAY(d.lastWriteDate)/10 + '0'; // перевод из целого в символ -- день
SB[9] = FAT_DAY(d.lastWriteDate)%10 + '0';
SB[10] = FAT_MONTH(d.lastWriteDate)/10 + '0'; // месяц
SB[11] = FAT_MONTH(d.lastWriteDate)%10 + '0';
SB[12] = (FAT_YEAR(d.lastWriteDate)%100)/10 + '0'; // последние две цифры года
SB[13] = FAT_YEAR(d.lastWriteDate)%10 + '0';
CRB = 0; // Сбрасываем индексы.
CWB = 0;
bBit = 15;
CalcTb(); // Вычисляем значение задержки на начало байта Tb
// Начинаем наполнять буфер
for (i=0; i<=3; i++){ // преамбула (4*(00H*25+55H*25))
for (j=0; j<=24; j++){
BUFF[lowByte(CWB)] = 0x00;
CWB++;
}
for (j=0; j<=24; j++){
BUFF[lowByte(CWB)] = 0x55;
CWB++;
}
}
Timer1.setPeriod(Tpp); // Выставляем период таймера
Timer1.start(); // Запускаем таймер............
for (BLt=BLe; BLt>=1; BLt--){ // Вывод блоков данных в цикле
CSz = BLs;
CSz += BLe;
CSz += BLt;
for (j=0; j<=15; j++) ToBUFF(0x00);// 00h*16
for (j=0; j<=3; j++) ToBUFF(0x55);// 55h*4
ToBUFF(0xE6); // E6h*1
for (j=0; j<=3; j++) ToBUFF(0x00);// 00h*4
for (j=0; j<=26; j++){ // заголовок блока
CSz += SB[j];
ToBUFF(SB[j]);
}
ToBUFF(BLs); // начальный блок
ToBUFF(BLe); // конечный блок
ToBUFF(BLt); // осталось блоков
lcd.setCursor(12, 0); // выводим на экран кол-во оставшихся блоков
lcd.print(BLt);
lcd.print(' ');
ToBUFF(CSz); // контр.сумма заголовка
for (Nst=0x80; Nst<=0x87; Nst++){ // вывод строк (8 шт.)
for (j=0; j<=3; j++) ToBUFF(0x00);// 00h*4
ToBUFF(0xE6); // E6h*1
CSs = Nst;
ToBUFF(Nst); // номер строки
CSs += CSz;
ToBUFF(CSz); // контр.сумма заголовка
// начинаем вывод строки данных
for (j=0; j<=31; j++){ // цикл на 32 байта
if (Nbt > 0){ // ещё есть данные?
St = dataFile.read(); // читаем очередной байт из файла
Nbt--;
}
else { // нет -- дополняем нулями
St = 0x00;
}
ToBUFF(St); // передаём считанный байт
CSs += St;
if (getPressedButton()!=BT_none) { // кнопка нажата?
Timer1.stop(); // Останавливаем таймер
dataFile.close(); // закрываем файл
return 1; // выход из ПП с ошибкой 1.
}
if (CRB_temp > CWB) { // проверка -- не обогнало ли чтение запись?
Timer1.stop(); // Останавливаем таймер
dataFile.close(); // закрываем файл
return 2; // выход из ПП с ошибкой 2.
}
}
ToBUFF(CSs); // контр.сумма строки
}
}
dataFile.close(); // закрываем файл
for (j=0; j<=31; j++) ToBUFF(0x00);// 00h*32 -- завершение вывода программы (?)
}
else return 3; // нет файла
WaitBuffer(); // ожидаем окончания вывода
return 0; // выход из ПП с кодом 0.
}
void ToBUFF(byte SBb){ // Подпрограмма записи байта в буфер
noInterrupts(); // запрет прерываний
CRB_temp = CRB; // сохраняем CRB во временную переменную
interrupts(); // разрешение прерываний
if (CWB > (CRB_temp + 255)) { // Если позиция записи больше, чем позиция чтения + размер буфера - 1
delay(Tpp); // Задержка (Tpp*1000 мкс = Tpp мс = 125 байт)
}
BUFF[lowByte(CWB)] = SBb;
CWB++;
}
void SendHalfBit() { // Подпрограмма вывода полубита по циклу таймера
byte Pd=PORTD;
if (bBit & 1){ // проверка индекса полубитов на чётность
if (( (Pd >> p)^( BUFF[lowByte(CRB)] >> (bBit >> 1) ))&1){ // Если состояние порта и выводимый бит разные
Pd ^= (1 << p); // инвертируем бит в позиции p
}
}
else{ // чётный -- просто инвертируем порт
Pd ^= (1 << p); // инвертируем бит в позиции p(=3)
}
PORTD = Pd; // вывод в порт p
if (bBit > 0) { // правим счётчики полубитов и байтов
bBit--;
if (bBit == 14) Timer1.setPeriod(Tpp); // Выставляем период таймера (биты)
}
else{
bBit = 15;
CRB++;
/*if (CRB > 200)*/ Timer1.setPeriod(Tpp+Tb); // Выставляем увеличенный период таймера (начало байта)
}
}
ISR(ANALOG_COMP_vect) // ПП обработки прерывания компаратора -- определяет и заносит полученные биты в буфер
{
unsigned long iMicros = micros();
unsigned long PPeriod = iMicros - iMicros_old;
iMicros_old = iMicros;
boolean Plong = false; // короткий полупериод по умолчанию
if (PPeriod < 1024) { // началось...
if (bBit < 16) { // если это не последний полубит
Plong = (PPeriod > (PPeriod_sred[PP]>>7)); // sred/128, =false, если тек. полупериод короткий
if (CWB <= 127) { // расчёт среднего значения, если меньше 256 байт считано
if (!Plong) { // если полупериод короткий
PPeriod_sred[PP] = (PPeriod_sred[PP]*CWB + PPeriod*192)/(CWB+1); // "среднее значение" = 1,5*128 от короткого полупериода
}
// else { // если полупериод длинный, берём PPeriod/2
// PPeriod_sred = (PPeriod_sred*CWB + PPeriod*3/4)/(CWB+1);
// }
}
}
else { // если последний полубит, вводим корректировку на задержку между байтами
Plong = (PPeriod > (PPeriod_sred[PP]*25/2432)); // с учётом увеличения периода между байтами, граница будет =([1,5]*25/19)/128 =1,97*Tpp
//Plong = (PPeriod > ((PPeriod_sred[PP]>>7) + 96)); // с учётом увеличения периода между байтами на 96 мкс
}
PP ^= 1; // инвертируем бит признака чётности полупериода
// расшифровка данных
if (Plong) { // получен длинный полупериод
B ^= 1; // инвертируем бит
A = (A<<1)+B; // заносим бит
bBit--; // уменьшаем счётчик полубитов
}
else { // получен короткий полупериод
if (bBit & 1) { // нечётный полубит
A = (A<<1)+B; // заносим бит
} // нечётный -- пропускаем
}
// корректировка счётчиков
if (bBit > 1) {
bBit--; // счётчик полубитов -1
}
else {
BUFF[lowByte(CWB)] = A; // заносим байт в буфер
CWB++; // счётчик байтов +1
A = 0; // обнуляем буфер для битов
bBit += 15; // = +16 -1
}
}
else { // был перерыв в сигнале
PPeriod_sred[0] = 98304; // берём заведомо большое среднее значение (192*512)
PPeriod_sred[1] = 98304; // берём заведомо большое среднее значение
CWB = 0; // начинаем запись с начала
bBit = 15;
A = 0;
B = 0;
PP = 1; // = нечётный полупериод
Pik = true; // есть сигнал!
}
}