Я разрабатываю программу генерации звука для Attiny85 u C с использованием Arduino IDE и AttinyCore и наткнулся на проблему, с которой никогда раньше не сталкивался.
Инициализация таймеров включает два таймера, Timer0 и Timer1. Первый используется для вывода ШИМ, второй - для временного прерывания по частоте дискретизации. Короче говоря, это дает мне два байта, OCR0A и OCR0B, изменяя которые, я могу выводить звук, и прерывание, которое вызывается прибл. 15 кГц. Все делается в одном файле: setup () инициализирует таймеры, l oop () отображает звук в sample1 и sample2, а ISR выводит звук на OCR0A и OCR0B.
#define F_CPU 8000000UL
#include "Arduino.h"
#include <avr/interrupt.h>
#include <stdlib.h>
#include <avr/io.h>
uint8_t sample1 = 0, sample2 = 0;
uint16_t phase1 = 0, phase2 = 0;
void setup() {
digitalWrite(5, HIGH); // Immedeately turn on this pull-up resistor for the reset pin
pinMode(0, OUTPUT); // Two outputs
pinMode(1, OUTPUT);
cli();
// Turn on PLL and wait while it stabilizes
bitSet(PLLCSR, PLLE);
delayMicroseconds(100);
while(!bitRead(PLLCSR, PLOCK));
bitSet(PLLCSR, PCKE);
bitSet(PLLCSR, LSM); //low speed mode, PLL @ 32MHz.
//Set up the TIM0 timer
TCCR0A = bit(WGM00) | bit(WGM01); //Mode: 011, fast PWM. IMHO regular CTC PWM is superior
TCCR0B = 0; //WGM02 is zero
TCCR0A |= bit(COM0A1); // Channel A: Non-inverting PWM mode (10)
TCCR0A |= bit(COM0B1); // Channel B: Non-inverting PWM mode (10)
bitSet(TCCR0B, CS00); // Timer frequency: CLKio/1 = 8MHz
//Set up the TIM1 timer
TCCR1 = 0; TCNT1 = 0; // Stop and clear the timer
GTCCR = bit(PSR1); //Reset the timer
OCR1A = 255; OCR1C = 255;
TIMSK = bit(OCIE1A); // Interrupt enable
TCCR1 = bit(CTC1) | bit(CS12); //Clock: PLL Clock / 8 = 32MHz / 8 = 4 MHz = CPU Clock / 2
//Final Timer1 config: reset on OCR1C compare
//Interrupt on OCR1A compare
//Frequency: 4MHz / 256 = 15625 Hz
sei();
}
ISR(TIMER1_COMPA_vect)
{
OCR0A = sample1;
OCR0B = sample2;
//Internal oscillators 1, 2
phase1 += 3*255;
phase2 += 3*400;
}
void loop() {
sample1 = phase1 >> 8;
sample2 = phase2 >> 8;
}
Making phase1, phase2, sample1, sample2 volatile помогает, но мой вопрос - почему?
Проблема в том, что при использовании LTO, -Os этот код не работает. Это можно исправить, либо переместив вычисление в прерывание, либо назначив результат вычисления прямо байтам OCR0A и OCR0B в функции Synthesis (), однако это не то поведение, которое я желаю, поскольку я хочу, чтобы прерывание было как можно короче.
Но, либо с -O0 (оптимизация полностью отключена), либо с отключенным LTO, этот код действительно работает, но размер fla sh значительно увеличился (2.3kb с -O0, 700B с -Os, LTO disable, 504b с включенным LTO).
Я мог бы отключить LTO, но мне это кажется грязным хаком. Мои вопросы:
Это грязный хакер для отключения LTO?
Нормально ли оптимизатор так резко менять поведение? Почему он работает без LTO?
Почему мне нужно сделать эти переменные изменчивыми и как определить, нужно ли мне делать переменные изменчивыми (кроме простого метода проб и ошибок или просто сделать их все энергозависимыми?)
Что еще я могу сделать, кроме изменения настроек оптимизатора и создания чего-то нестабильного?
Может быть, это кодировка проблема стиля?
Есть какие-нибудь советы по написанию кода, который оптимизатор не нарушает?