Не работает millis Attiny13a (MicroCore) в Proteus

Здравствуйте. Ещё одна проблемка у меня, при симуляции проекта.
Attiny13a
MicroCore
Ошибка в Proteus
PC=0x002E. [AVR WATCHDOG] Incorrect watchdog timer setup sequence. [U1]

Нельзя исправить?

Наверное, можно, но нужно знать что там у Вас за

Мне рекомендовали, на форуме, использовать MicroCore

Сейчас скачала DIY ATtiny
В Proteus заработало

Спойлер

Этот текст будет скрыт

Как узнать?)

Ну, как, код посмотреть.

На миллис ругается

uint32_t tmr;
bool tmr_flag;

void setup() {
// put your setup code here, to run once:

}

void loop() {

if (tmr_flag && millis() - tmr >= 2000) {
tmr_flag = false;

}

}

Дык надо код millis смотреть. Он там есть в адд-оне

Замените строки в файле
packages\MicroCore\hardware\avr\2.2.0\cores\microcore\millis.S

строки

.section .init8
ldi r16, 1<<WDTIE
out WDTCR, r16
sei

На строки

.section .init8
ldi r16, (1<<WDTIF) | (1<<WDTIE) | (1<<WDCE) | (1<<WDE)
out WDTCR, r16
ldi r16, (1<<WDTIF) | (1<<WDTIE) | (0<<WDCE) | (0<<WDE)
out WDTCR, r16
sei

Хотя для изменения WDTIE особая запись не требуется. Вероятно это глюк протеуса. (Похожий глюк есть и в 328).

Tiny13 какой частотой тактируется?
Ядро DIY ATtiny врёт с millis() при 1,2 мгц.

Спасибо, попробую.

9,6 МГц

вдруг кто будет перечитывать: такой код не имеет смысла. он не делает, то, что как бы задумано, потому и не работает.

скорее всего имелось ввиду вот так:
WDTCSR = (WDTCSR & ~((1<<WDCE) | (1<<WDE))) | (1<<WDTIF) | (1<<WDTIE);

А так в Proteus нормально моделируются прерывания от WDT. Но проблема в том, что штатный макрос wdt_enable(period); для AVR не приводит к изменению регистра WDTCSR так, как нужно, и приходится писать свою функцию. Например что-то вроде

wdt_reset();
cli();
MCUSR &= ~(1<<WDRF);
WDTCSR |= (1<<WDCE) | (1<<WDE);
WDTCSR = 0b01100000; // 4s    
sei(); 

(для AVR 382p)

тогда все моделируется

Этот код был проверен на работоспособность. И он работает как задумано.
Задумано было внесение изменений в ядро. Чтобы один и тот же ИМЕЮЩИЙСЯ и РАБОТАЮЩИЙ на железе скетч, одинаково выполнялся и в Proteus и на железе. Без этих изменений, он не запускался в Proteus.
Да, как альтернатива можно было не трогать ядро и внести изменения в скетч, с учетом особенностей Proteus.

Так пишется лишь для наглядности, при инициализации, когда регистры и так равны 0.

3 лайка

Вы его не поняли. Он имел в виду, что код

 |  (0 << нечто)

не имеет никакого смысла от слова совсем, т.к. он не делает ничего. Скорее всего, компилятор его выбрасывает. Тогда зачем его писать и перегружать чтение программы.

Этот код никак не влияет на регистры. Вообще никак. Он ничего не делает. Он просто загромождает программу.

Ну так я и пишу

Просто, чтобы показать тек. состояние регистров.
Я иногда так делаю, но чаще не делаю))

1 лайк

В исходном тексте было написано
ldi r16, (1<<WDTIF) | (1<<WDTIE) | (0<<WDCE) | (0<<WDE)

И как уже написал Дим-мычъ, нулевые значения указаны для наглядности, что они сброшены.

Но на ответ меня сподвигло не замечание, что так писать “не красиво”, а замечание “Потому и не работает

2 лайка
#include <avr/io.h>  // Подключение библиотеки для работы с регистрами ввода-вывода
#include <avr/interrupt.h>  // Подключение библиотеки для работы с прерываниями

volatile unsigned long timer_millis = 0;  // Глобальная переменная для хранения миллисекунд

// Обработчик прерывания по переполнению таймера 0
ISR(TIM0_OVF_vect) {
timer_millis++;  // Увеличиваем счетчик миллисекунд при каждом переполнении таймера
}

// Функция настройки таймера
void setup_timer() {
// Настройка предделителя таймера 0
// Для частоты 9.6 МГц с предделителем 64
// CS02=0, CS01=1, CS00=1 - предделитель 64
TCCR0B = (1<<CS01) | (1<<CS00);  // Установка битов CS01 и CS00 для предделителя 64

// Разрешение прерывания по переполнению таймера 0
TIMSK0 = (1<<TOIE0);  // Установка бита TOIE0 для разрешения прерывания

sei();  // Разрешение глобальных прерываний
}

// Пользовательская функция millis()
unsigned long millis_custom() {
// Корректировка значения для преобразования в миллисекунды
// Частота прерываний = 9600000/64/256 = 58.59 Гц
// Коэффициент коррекции = 1000/58.59 ≈ 17
return timer_millis * 17;  // Возвращаем время в миллисекундах
}

// Объявление глобальных переменных
uint32_t tmr;  // Переменная для хранения времени отсчета
bool tmr_flag = false;  // Флаг состояния таймера

// Функция setup() - выполняется один раз при старте
void setup() {
setup_timer();  // Инициализация таймера
tmr = millis_custom();  // Запоминаем начальное время
}

// Функция loop() - выполняется в бесконечном цикле
void loop() {
// Проверяем, прошло ли 2000 миллисекунд (2 секунды)
if (millis_custom() - tmr >= 2000) {
tmr = millis_custom();  // Обновляем время отсчета
tmr_flag = !tmr_flag;  // Инвертируем состояние флага
// Здесь можно добавить код, который должен выполняться каждые 2 секунды
}
}

может поможет…

timer_millis изменяется в прерывании, поэтому такой код не катит)

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/atomic.h>

volatile unsigned long timer_millis = 0;
volatile uint16_t timer_accumulator = 0;

ISR(TIM0_OVF_vect) {
    // Для 9.6 МГц с предделителем 64:
    // Частота таймера: 9600000 / 64 = 150000 Гц
    // Период прерывания: 256 тактов
    // Накопление для 1 мс: 150 тактов (150000 / 1000)
    timer_accumulator += 256;
    
    if (timer_accumulator >= 150) {
        timer_accumulator -= 150;
        timer_millis++;
    }
}

void setup_timer() {
    TCCR0B = (1<<CS01) | (1<<CS00); // Предделитель 64
    TIMSK0 = (1<<TOIE0); // Разрешить прерывание по переполнению
    sei(); // Разрешить глобальные прерывания
}

unsigned long millis_custom() {
    unsigned long temp;
    ATOMIC_BLOCK(ATOMIC_FORCEON) {
        temp = timer_millis;
    }
    return temp;
}

uint32_t tmr;
bool tmr_flag = false;

void setup() {
    setup_timer();
    tmr = millis_custom();
}

void loop() {
    if (millis_custom() - tmr >= 2000) {
        tmr = millis_custom();
        tmr_flag = !tmr_flag;
    }
}

?)