// ПРОГРАММА НЕ РАБОТАЕТ
// 2022/07 - в симуляторе не реализован модуль захвата таймеров.
// Для Ардуино на ATmega 328P 16 MHz и совместимых. Для индикации используется встроенный светодиод L
//
// 2022/07 заметил, что на Proteus 8.6 SP2 Build 23525
// Неправильно работает UART на AVR конкротно ATmega328P
// Для тактовой 16 Мгц правильно работает только начиная с 2400
// Чем меньше тактовая, тем меньший baud работает корректно.
//
// Результат измерений показал, что модель протеуса учитывает только 10 разрядов регистра UBRR
// Т.е. значения регистра UBRR больше 1023 отрабатываются не правильно.
// По даташиту и реально этот регистр 12 разрядный
//
// Задача программы автоматически определить частоту с которой начинается ошибка.
//
// Видим на терминале предложение послать Y для начала измерений.
//
static const uint8_t pinBaudIn = 8; // вход захвата подключить проводом к RX.
//static const uint8_t pinButton = A0; // Вход для подключения кнопки
static const uint8_t pinPWM = 11; // Выход PWM (OC2A) для возможности тестового сигнала
static const uint32_t TerminalBaudRate = 9600;
static const uint32_t StartBaudRate = 9600; // Начальная тестируемая скорость
static const int16_t StepBaudRate = 100; // Шаг уменьшения
static const int16_t NumSteps = 2; // Количество измерений (шагов)
struct resultStruct {
uint32_t baudRate; // Скорость в бодах.
uint32_t realLenth; // Измеренная длительность в мкс
int16_t UBRR; // реальное значение регистра скорости
uint8_t errorFlag; // флаг ошибки
};
resultStruct resData[NumSteps]; // массив структур для результата измерений
volatile uint16_t captStart, captStop; // Для захваченного счетчика
volatile uint8_t flagCaptError; // Для захваченного счетчика
void setup()
{
pinMode(LED_BUILTIN, OUTPUT);
pinMode(pinBaudIn, INPUT); //
// pinMode(pinButton, INPUT_PULLUP); // Для управления как кнопкой.
Serial.begin(TerminalBaudRate); // Общаться будем на 9600. На время измерений отключаем.
Serial.println(F("-----START-----"));
Serial.println(F("For Arduino UNO and compatible."));
Serial.println(F("During measurements, random values may be displayed in the terminal"));
Serial.println(F("Send Y to start. And wait results"));
//Serial.println(F_CPU);
Serial.println("");
// Настройка первого таймера, делитель = 64, с прерыванием по захвату, без шумодава.
TCCR1A = 0; // PWM отключен, TOP не используется. COM1A1 COM1A0 COM1B1 COM1B0 --- --- WGM11 WGM10
TCCR1B = 3; // ICNC1=0 ICES1=0 CS11=1 CS10=1 без шумодава, по спаду, запущен без делителя. ICNC1 ICES1 --- WGM13 WGM12 CS12 CS11 CS10
TCCR1C = 0; // Не используется (Force Output Compare for Channel A / B)
TIMSK1 = 1<<ICIE1;// Прерывания по захвату --- --- ICIE --- --- OCIE1B OCIE1A TOIE1
// TIFR1 --- --- ICF1 --- --- OCF1B OCF1A TOV1
}
void loop(){
Serial.println("");
Serial.println(F("When ready send Y "));
{
uint8_t myByte;
while(true) {
if(Serial.available()) {
myByte = Serial.read();
if(myByte == 'y' || myByte == 'Y')
break;
}
}
}
for(int i = 0; i < NumSteps; i++){
int32_t baudRate = StartBaudRate - i*StepBaudRate;
if (baudRate <= 0){
resData[i].baudRate = 0;
resData[i].realLenth = 0;
resData[i].UBRR = 0;
continue;
}
resData[i].baudRate = baudRate;
Serial.end(); //
Serial.begin(baudRate);
resData[i].UBRR = UBRR0H*256 + UBRR0L;
noInterrupts();
bitClear(TCCR1B, ICES1); // захват по спаду
TIFR1 = 1<<ICF1; // Пишут, что надо очистить флаг прерывания после смены условия
bitSet(TIMSK1, ICIE1); // Разрешаем прерывание (его сброс = признак окончания измерения).
interrupts();
Serial.write(0x00); //
Serial.write(0x00); //
while(bit_is_set(TIMSK1,ICIE1) && flagCaptError == 0) { // Ждем. Конец цикла измерения будет когда Прерывание по захвату запрещено.
// Здесь можно добавить СВОЙ контроль времени. Например по флагам переполнения.
if(bit_is_set(TCCR1B, ICES1))
digitalWrite(LED_BUILTIN, HIGH);
}
bitClear(TIMSK1, ICIE1); // при ошибке флаг может быть не сброшен
if (flagCaptError != 0){ // Если была ошибка, то записываем нули в старт и стоп
captStart = 0;
captStop = 0;
}
resData[i].errorFlag = flagCaptError;
resData[i].realLenth = (captStop - captStart)*4; // т.к. используется делитель на 64 то для мкс надо умножить на 4
Serial.end(); //
}
Serial.begin(TerminalBaudRate);
Serial.println("");
Serial.println(F("---RESULT---"));
for(int i = 0; i < NumSteps; i++){
Serial.print("Baud ");Serial.print(resData[i].baudRate);
Serial.print(" UBRR ");Serial.print(resData[i].UBRR);
//Serial.print(" Expec-mks ");Serial.print((resData[i].UBRR+1)*9UL/2);
Serial.print(" Lenth-mks ");Serial.print(resData[i].realLenth);
Serial.println("");
}
Serial.println(F("---END RESULT---"));
}
// Пока код без стремления к минимальной задержке.
ISR(TIMER1_CAPT_vect)
{
if(bit_is_clear(TCCR1B, ICES1)){ // если захват по спаду
captStart = ICR1; // сохраняем счетчик
bitSet(TCCR1B, ICES1); // захват по нарастанию
TIFR1 = 1<<ICF1; // Пишут, что надо очистить флаг прерывания после смены условия bitSet(TIFR1, ICF1);
if(bit_is_set(PORTB, PORTB0)) // Если уже высокий, то ошибка
flagCaptError = 1; // ставим признак ошибки
}
else { // если захват по нарастанию
captStop = ICR1; // сохраняем счетчик
bitClear(TIMSK1, ICIE1); // Запрет прерывания он-же признак окончания измерения.
}
}