Таймеры/счётчики в AVR
Re: Таймеры/счётчики в AVR
[uquote="ciaas",url="/forum/viewtopic.php?p=4485797#p4485797"]А сделал я как предложила Just_Fluffy. Перфект! и респект ! Не знаю как поставить плюсик в телефоне.[/uquote]
В ПРОЕКТЕ ссылку на Just_Fluffy только нужно не забыть указать. А то ведь всё я, да я...)
В ПРОЕКТЕ ссылку на Just_Fluffy только нужно не забыть указать. А то ведь всё я, да я...)
Re: Таймеры/счётчики в AVR
Без этого никак. Будет ссылка 
Re: Таймеры/счётчики в AVR
добрый день всем!
спрошу тут, раз есть тема и такая историческая...
камень mega328
таймер0 настроен на прерывание по переполнению каждые 8 мкС (125 кГц) (8 МГц / 64);
Таймер работает в режиме 3 - Fast PWM, TOP=0xFF, upd. of OCRx at BOTTOM (0x00), TOV flag set on MAX (0xFF))
на таймере работает измерение задержек (millis)
остальные таймеры заняты другими вещами..
хотелка: АЦПировать с частотой в 500 Гц
вопрос:
могу ли я получить прерывание от того же таймера по сравнению значения - ISR (TIMER0_COMPA_vect)
путём включения соотв. бита TIMSK0 |= (1 << OCIE0A), и записью значения в регистр OCR0A: OCR0A = 249
?
т.е. прерывание по переполнению по-прежнему будет происходить, но хотелось б чтобы ещё происходило и прерывание по сравнению. это допустимо в режиме "3"?
кстати, разные калькуляторы дают разное значение OCR, кто считается более "правильным"?


спрошу тут, раз есть тема и такая историческая...
камень mega328
таймер0 настроен на прерывание по переполнению каждые 8 мкС (125 кГц) (8 МГц / 64);
Таймер работает в режиме 3 - Fast PWM, TOP=0xFF, upd. of OCRx at BOTTOM (0x00), TOV flag set on MAX (0xFF))
на таймере работает измерение задержек (millis)
остальные таймеры заняты другими вещами..
хотелка: АЦПировать с частотой в 500 Гц
вопрос:
могу ли я получить прерывание от того же таймера по сравнению значения - ISR (TIMER0_COMPA_vect)
путём включения соотв. бита TIMSK0 |= (1 << OCIE0A), и записью значения в регистр OCR0A: OCR0A = 249
?
т.е. прерывание по переполнению по-прежнему будет происходить, но хотелось б чтобы ещё происходило и прерывание по сравнению. это допустимо в режиме "3"?
кстати, разные калькуляторы дают разное значение OCR, кто считается более "правильным"?
- Just_Fluffy
- Вымогатель припоя
- Сообщения: 532
- Зарегистрирован: Ср июн 29, 2022 16:25:45
Re: Таймеры/счётчики в AVR
timex, у вас таймер переполняется каждые 8 мкс.
500 гц - это 2000 мкс, делаете в обработчике прерываний счетчик от 0 до (250-1) - и стартуете АЦП один раз в 250 прерываний
500 гц - это 2000 мкс, делаете в обработчике прерываний счетчик от 0 до (250-1) - и стартуете АЦП один раз в 250 прерываний
Белая и Пушистая
Re: Таймеры/счётчики в AVR
Just_Fluffy, это понятно.. но хочется сделать по красоте, а код обработчика и инициализации таймера на 8 мкс не хотелось бы трогать (оно библиотечное).
у меня сейчас по осциллограмме 479.9 Гц происходит прерывание по сравнению OCR, с любым значением OCR - не пойму пока почему...
ре-инициализация уже тикающего таймера0:
(когда АЦПировать надоест - я сбрасываю бит прерывания по сравнению у регистра маски прерываний таймера0)
обработчик:
у меня сейчас по осциллограмме 479.9 Гц происходит прерывание по сравнению OCR, с любым значением OCR - не пойму пока почему...
ре-инициализация уже тикающего таймера0:
Код: Выделить всё
//настройка прерывания TC0
cli(); //выключаем возможное прерывание по переполнению TC0 (работает для millis и micros из chrono.cpp)
TIFR0 = (1<<TOV0); //reset flag int of overflow
OCR0A = 0xF9;
TIMSK0 |= (1 << OCIE0A); //TC0 Output Compare Match A Interrupt Enable (only interrupt, no OC0A pin toggle)
sei();
обработчик:
Код: Выделить всё
//прерывание по сравнению значения TC0
ISR (TIMER0_COMPA_vect)
{
//do some...
ADCSRA |= (1<<ADSC); //start conversion
while (ADCSRA & (1<<ADSC)); //wait for conversion to complete
//do some...
}
- Just_Fluffy
- Вымогатель припоя
- Сообщения: 532
- Зарегистрирован: Ср июн 29, 2022 16:25:45
Re: Таймеры/счётчики в AVR
вот эта фраза меня немного в заблуждение ввела.timex писал(а):таймер0 настроен на прерывание по переполнению каждые 8 мкС (125 кГц) (8 МГц / 64);
У вас таймер тактируется от 8 МГц с делителем 64 ?
Тогда прерывание по переполнению возникает не каждые 8 мкс, а в 256 раз реже.
8 мкс - это период тактовой частоты на вход счетчика таймера.
Соответственно, 8 мгц / 64 = 125 кгц. У вас таймер в режиме 3 - FastPWM с ТОР = 0xFF. соответственно, таймер считает от 0 до 255 каждые 8 мкс.
И прерывание по переполнению возникает каждые 8*256 = 2048 мкс - 2,048 мс. 1/2048 = ~488,3 Гц - все по честному.
И как бы вы не настраивали регистр сравнения - таймер все равно будет молотить полный цикл в 256 отсчетов.
Вам нужно как то уменьшать ТОР. Например, используя другой режим. Например, 7 - FastPWM, TOP in OCR0A - тогда можно записать в OCR0A значение 250-1 и получить период таймера (64*250)/8МГц = 0,002 с = 2 мс. Т.е. таймер будет молотить как раз на ваших 500 гц
Но, судя по некоторым вашим словам - у вас не просто атмега, у вас ардуина с ее IDE, millis-ами, analogWrite-ами...
И следует быть готовым к тому, что функции ардуины, использующие таймер 0, мгут работать не всегда так, как ожидалось бы от них.
Вроде на сайте у Гайвера было про работу с таймерами в применении к ардуине.
Белая и Пушистая
- Engineer_Keen
- Друг Кота
- Сообщения: 3868
- Зарегистрирован: Пт янв 29, 2010 10:27:40
- Откуда: Москва
Re: Таймеры/счётчики в AVR
"при выполнении данного кода ни одного котика таймера не пострадало..."остальные таймеры заняты другими вещами..
хотелка: АЦПировать с частотой в 500 Гц
ATMEGA328, 8MHz
PS: сорян за ассемблер, но думаю принцип понятен
//----------------------------------------//
.equ ADC_TMR_CONST=19 //константа для программного таймера ADC_TMR_CONST=(Fcpu/делитель АЦП/13/500Гц)
.dseg
ADC_TMR: .byte 1 //переменная программного таймера
.cseg
//в коде начальной инициализации:
...
LDI R16,(1<<ADLAR)|0 //настройка опоры, канала, выравнивания
STS ADMUX,R16
LDI R16,(1<<ADEN)|(0<<ADATE)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(0<<ADPS0) //включаем АЦП, прерывание
STS ADCSRA,R16 //и делитель, получаем тактирование 125кГц (для АЦП надо от 50 до 200), при этом само АЦП требует 13 тактов для преобразования
LDI R16,ADC_TMR_CONST //настраиваем программный таймер (точнее делитель)
STS ADC_TMR,R16
...
//в ISR обработчика АЦП:
IN R3,SREG //сохраняем статус
PUSH R16 //сохраняем R16
LDI R16,(1<<ADEN)|(0<<ADATE)|(1<<ADSC)|(1<<ADIE)|(1<<ADPS2)|(1<<ADPS1)|(0<<ADPS0) //перезапуск АЦП
STS ADCSRA,R16
LDS R16,ADC_TMR //уменьшаем таймер
DEC R16
BRNE NO_RESET_ADC_TMR //пока не обнулится
LDI R16,ADC_TMR_CONST //если обнулился, инициализируем по-новому
STS ADC_TMR,R16
LDS R16,ADCH //и вот тут читаем АЦП, эта строчка при тактовой в 8МГц будет выполнятся с частотой в 500Гц
POP R16 //восстанавливаем регистры
OUT SREG,R3
RETI //выход
NO_RESET_ADC_TMR:
STS ADC_TMR,R16 //а тут просто сохраняем текущее значение таймера
POP R16
OUT SREG,R3
RETI
//----------------------------------------//
Неправильно собранная из неисправных деталей схема нуждается в отладке и сразу не работает... (С)
Re: Таймеры/счётчики в AVR
[uquote="Just_Fluffy",url="/forum/viewtopic.php?p=4584555#p4584555"]... Например, используя другой режим. Например, 7 - ...[/uquote]
спасибо, уважаемый, я и забыл что тики-тиками, а прерывание по переполнению в 256 раз реже)
надо читать доки внимательнее)
пока я сделал так как и рекомендовали, изначально изменил режим таймера на 7, OCR0A = 250-1 и
и изменил дефайн для подсчёта задержек
с
на
но тем не менее остался вопрос, если записать что-то в OCR0A, например 249, включить режим 3 таймера0 (waveform generation mode (3): Fast PWM, top: 0xFF, upd. of OCRx at: BOTTOM, TOV flag set on: MAX), включить прерывание по переполнению И прерывание по сравнению OCR0A,
то после запуска таймера по-идее сначала возникнет прерывание по сравнению (и не важно, успеем ли из него выйти по RETI или нет), а следующим (через 7 тактов, т.к. 256-249=7) возникнет прерывание по переполнению, верно?
даташит как-то обходит стороной этот вопрос..
и второй вопросик, в режиме 7 таймера0 (waveform generation mode (7): Fast PWM, top: OCR0A, upd. of OCRx at: BOTTOM, TOV flag set on: TOP)
в если в нём будут включены прерывания по переполнению И по сравнению, то они будут выполнятся оба, согласно их приоритетам, верно?
спасибо, уважаемый, я и забыл что тики-тиками, а прерывание по переполнению в 256 раз реже)
надо читать доки внимательнее)
пока я сделал так как и рекомендовали, изначально изменил режим таймера на 7, OCR0A = 250-1 и
и изменил дефайн для подсчёта задержек
с
Код: Выделить всё
#define clockCyclesPerMicrosecond() ( F_CPU / 1000000L )
#define clockCyclesToMicroseconds(a) ( (a) / clockCyclesPerMicrosecond() )
//#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 256))Код: Выделить всё
#define MICROSECONDS_PER_TIMER0_OVERFLOW (clockCyclesToMicroseconds(64 * 250))то после запуска таймера по-идее сначала возникнет прерывание по сравнению (и не важно, успеем ли из него выйти по RETI или нет), а следующим (через 7 тактов, т.к. 256-249=7) возникнет прерывание по переполнению, верно?
даташит как-то обходит стороной этот вопрос..
и второй вопросик, в режиме 7 таймера0 (waveform generation mode (7): Fast PWM, top: OCR0A, upd. of OCRx at: BOTTOM, TOV flag set on: TOP)
в если в нём будут включены прерывания по переполнению И по сравнению, то они будут выполнятся оба, согласно их приоритетам, верно?
- Starichok51
- Модератор
- Сообщения: 19039
- Зарегистрирован: Сб авг 14, 2010 15:05:51
- Откуда: г. Озерск, Челябинская обл.
Re: Таймеры/счётчики в AVR
если ты запишешь в OCR0A 249 и включишь режим 7 (top: OCR0A), то после 249 в счетчик запишется 0, и счетчик никогда не досчитает до 256, чтобы сработало переполнение.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Re: Таймеры/счётчики в AVR
Да. Сначала TIMER0 COMPA, затем TIMER0 OVF.timex писал(а):...они будут выполнятся оба, согласно их приоритетам, верно?
- Just_Fluffy
- Вымогатель припоя
- Сообщения: 532
- Зарегистрирован: Ср июн 29, 2022 16:25:45
Re: Таймеры/счётчики в AVR
В принципе, верно, если сравнение попадает в период пересчета таймера.timex писал(а):по-идее сначала возникнет прерывание по сравнению (и не важно, успеем ли из него выйти по RETI или нет), а следующим (через 7 тактов, т.к. 256-249=7) возникнет прерывание по переполнению, верно?
Но. Если не принимать специальных мер, то внутри обработчика прерывания другие прерывания запрещены. Поэтому выполнится сначала первое прерывание (а будет оно выполняться тактов 25 минимум (вход/выход, сохранение/восстановление регистров...), потом, после выхода из прерывания, выполнится минимум одна команда основной программы, а потом уже, если есть активные флаги прерываний - будет выполняться следующее прерывание.
Согласно их очередности возникновения. Если же в процесе выполнения обработчика прерывания возникает несколько других прерываний - то да, они будут выполняться согласно их приоритетности, определенной таблицей прерываний. Т.е. чем меньше номер прерывания, тем выше приоритет. И этот приоритет в 8-битных классических АВРках не редактируется.они будут выполнятся оба, согласно их приоритетам, верно?
уважаемая...timex писал(а):спасибо, уважаемый
Белая и Пушистая
Re: Таймеры/счётчики в AVR
Искренне прошу помощи, не понимаю как реализовать задержку в 1 секунду на ATmega32..... Задача у меня такая: «Будильник». Исходное значение цифр на семисегментном индикаторе «9». Нажатием кнопки на входе РА0 запускается таймер с обратным счетом: через 1 секунду индицируется цифра «8» и т.д. до «0». При достижении «0» включается светодиод на выходе РВ0. Я наверстал что-то по интернет гайдам, но по итогу ни к чему не пришёл(
В результате компиляции этого чуда получаю вот такую ошибку: AVR Simulator: Invalid opcode 0xffff at address 0x005858
В самой AVR код запускается и работает, но вот в Proteus схема вообще не включается

Код: Выделить всё
.include "m32def.inc"
; Определение констант для таймера
.def counter = r16
.def temp = r17
.equ F_CPU = 8000000 ; Частота процессора 8 МГц
.equ DELAY = F_CPU/1024 ; Делитель для 1 секунды
.org 0x00
rjmp init
.org OVF1addr
rjmp timer_interrupt
init:
; Инициализация стека
ldi temp, low(RAMEND)
out SPL, temp
ldi temp, high(RAMEND)
out SPH, temp
; Инициализация портов
ldi temp, 0x00
out DDRA, temp ; Порты A на ввод
ldi temp, 0xFF
out DDRC, temp ; Порты C на вывод
ldi temp, 0xFF
out DDRB, temp ; Порты B на вывод
; Начальное значение счетчика
ldi counter, 9
out PORTC, counter
; Разрешить прерывания
sei
main_loop:
; Проверка нажатия кнопки
sbis PINA, 0
rcall start_timer
rjmp timer_interrupt
start_timer:
; Настройка таймера для задержки в 1 секунду
ldi temp, high(DELAY)
out TCNT1H, temp
ldi temp, low(DELAY)
out TCNT1L, temp
ret
timer_interrupt:
; Уменьшение счетчика и обновление индикатора
dec counter
out PORTC, counter
cpi counter, 0
brne timer_interrupt
; Включение светодиода
sbi PORTB, 0
retiВ самой AVR код запускается и работает, но вот в Proteus схема вообще не включается

Re: Таймеры/счётчики в AVR
Не верю. Вы не запустили таймер, не разрешили прерывания. Даже если это будет выполнено, таймер будет отсчитывать не одну секунду, а гораздо больше. Как вариант отсчета 9 секунд с использованием режима CTC.
Код: Выделить всё
.include "m32def.inc"
; Определение констант для таймера
.def counter = r16
.def temp = r17
.equ F_CPU = 8000000 ; Частота процессора 8 МГц
.equ DELAY = F_CPU/1024 ; Делитель для 1 секунды
.org 0x00
rjmp init
.org $000E
rjmp timer_interrupt
init:
; Инициализация стека
ldi temp, low(RAMEND)
out SPL, temp
ldi temp, high(RAMEND)
out SPH, temp
; Инициализация портов
ldi temp, 0x00
out DDRA, temp ; Порты A на ввод
SBI PORTA,0 ;включить подтяжку кнопки
ldi temp, 0xFF
out DDRC, temp ; Порты C на выводPINA 0 25
ldi temp, 0xFF
out DDRB, temp ; Порты B на вывод
; Начальное значение счетчика
ldi counter, 9
out PORTC, counter
start_timer:
; Настройка таймера для задержки в 1 секунду
LDI R22,HIGH(DELAY)
OUT OCR1AH,R22
LDI R22,LOW(DELAY)
OUT OCR1AL,R22
LDI R22,1<<OCIE1A
OUT TIMSK,R22
LDI R22,1<<WGM13|1<<WGM12 ;CTC
OUT TCCR1B,R22
; SBR R22,1<<CS10 ;F_CPU/1
SBR R22,1<<CS12|1<<CS10 ;F_CPU/1024
; Разрешить прерывания
sei
main_loop:
; Проверка нажатия кнопки
sbic PINA, 0
rjmp main_loop
CBI PORTB,0 ;выключить светик
OUT TCCR1B,R22
SBIS PORTB,0
RJMP PC-1
sbis PINA, 0
rjmp PC-1
RJMP INIT
;*************************************************
timer_interrupt:
; Уменьшение счетчика и обновление индикатора
dec counter
out PORTC, counter
BRNE OUT_TIME
; Включение светодиода
sbi PORTB, 0
OUT_TIME:
reti
.exit
Re: Таймеры/счётчики в AVR
большое спасибо, надеюсь смогу откорректировать код самостоятельно
- Эйлер Леонард
- Встал на лапы
- Сообщения: 104
- Зарегистрирован: Пн ноя 04, 2019 09:58:29
- Откуда: г. Нижний Тагил Свердл. обл.
Re: Таймеры/счётчики в AVR
Добрый вечер. Моя задача относится к разряду "Помигать светодиодом". Это если рассматривать задачу как упрощенную модель. На самом же деле это драйвер открытия/закрытия топливной форсунки инжекторной системы фазированного распределенного впрыска четырехцилиндрового двигателя внутреннего сгорания. В принципе задачу я решил. Но, как говорится, тупо и в лоб. Во всяком случае в протеусе все работает. В целом написание прошивки для ЭБУ я решил по возможности разделить на отдельные блоки. Вычислительная часть(Master), где надо много и быстро обрабатывать данные с датчиков и исполнительный блок(Slave ATtiny88), который работает с уже готовым значением длительности для открытого состояния форсунки. Но у меня получилось четыре раба и каждый раб знает когда открыть форсунку и на какую длительность. Рабы работают слаженно и вполне покладисто. Каждый использует delayMicrosecond(). Поскольку от раба никакой другой задачи не требуется как открыть/закрыть одну форсунку, то блокирующий delay вполне себе оправдан. Но содержать такую толпу рабов как то, не то что бы накладисто, а как-то не прилично. Хотелось бы иметь одного умного раба, который работал на таймере и управлял всеми четырьмя форсунками. Каждая форсунка открывается по сдвигом по фазе на четверть периода. Сразу же напрашивается таймер, который инкриминирует в своем прерывании переменную от которой раб откусывает значение длительности открытого состояния форсунки, ну там - зафиксировать текущее значение, сравнить, ну и всё такое... В прикрепленном файле я для себя нарисовал картинку-шпаргалку. Может быть у кого-то есть мыслишка на этот счет или полезная ссылка.
Код Мастера SPI.
Код Slave SPI
void pulseMicroseconds(uint16_t us)
void delayMicroseconds(uint16_t us)
Код Мастера SPI.
Спойлер
Код: Выделить всё
/* ATmega328P AtmelStudio -std=c++14 */
#define F_CPU 1000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <stdlib.h>
#include "src\GPIO.h"
GPIO<PIN::PinD2> INT_0;
GPIO<PIN::PinD3> INT_1;
GPIO<PIN::PinC0> ss0;
GPIO<PIN::PinC1> ss1;
GPIO<PIN::PinC2> ss2;
GPIO<PIN::PinC3> ss3;
GPIO<PIN::MOSI> mosi;
GPIO<PIN::MISO> miso;
GPIO<PIN::SCK> sck;
uint16_t transfer16(uint16_t data);
volatile size_t k;
ISR(INT0_vect) {
k = 0;
}
volatile uint16_t p = 3000;
ISR(INT1_vect) {
switch(k) {
case 0: {
ss0.write(0);
transfer16(p);
ss0.write(1);
break;}
case 1: {
ss1.write(0);
transfer16(p);
ss1.write(1);
break;}
case 2: {
ss2.write(0);
transfer16(p);
ss2.write(1);
break;}
case 3: {
ss3.write(0);
transfer16(p);
ss3.write(1);
break;}
}
k++;
}
//================
int main(void){
cli();
ss0.output(); ss0.write(1);
ss1.output(); ss1.write(1);
ss2.output(); ss2.write(1);
ss3.output(); ss3.write(1);
mosi.output(); mosi.write(0);
miso.input(), miso.pullup();
sck.output(); sck.write(0);
/**/
// SPI2X SPR1 SPR0 Тактовая частота SCK
// 1 0 0 fosc/2
// 0 0 0 fosc/4
// 1 0 1 fosc/8
// 0 0 1 fosc/16
// 1 1 0 fosc/32
// 0 1 0 fosc/64
// 1 1 1 fosc/64
// 0 1 1 fosc/128
// -------------------------------------------- fosc/8 ->
// SPIE
// |SPE
// ||DORD
// |||MSTR
// |||| CPOL
// |||| |CPHA
// |||| ||SPR1
// |||| |||SPR0
// |||| ||||
SPCR = 0b0101'0001;
SPSR |= (1 << SPI2X);
//SPSR &= ~(1 << SPI2X);
INT_0.input(); INT_0.pullup();
INT_1.input(); INT_1.pullup();
EIMSK |= (1<<INT1)|(1<<INT0);
EICRA |= (1<<ISC11)|(1<<ISC10)|(1<<ISC01)|(1<<ISC00); // Нарастающий фронт INT1, INT0 генерирует прерывание.
sei();
while (1){
}
return 0;
}
// =============
uint16_t transfer16(uint16_t data) {
union { uint16_t val; struct { uint8_t lsb; uint8_t msb; }; } in, out;
in.val = data;
if ( !( SPCR & (1 << DORD) ) ) {
SPDR = in.msb;
asm volatile("nop");
while (!(SPSR & (1 << SPIF))) ;
out.msb = SPDR;
SPDR = in.lsb;
asm volatile("nop");
while (!(SPSR & (1 << SPIF))) ;
out.lsb = SPDR;
} else {
SPDR = in.lsb;
asm volatile("nop");
while (!(SPSR & (1 << SPIF))) ;
out.lsb = SPDR;
SPDR = in.msb;
asm volatile("nop");
while (!(SPSR & (1 << SPIF))) ;
out.msb = SPDR;
}
return out.val;
}
//============
Спойлер
Код: Выделить всё
/* ATtiny88 AtmelStudio -std=c++14 */
#define F_CPU 1000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdlib.h>
#include "D:\lib\SPI\SPI.hpp"
GPIO<PIN::PinD5> jet;
// SPIE
// |SPE
// ||DORD
// |||MSTR
// |||| CPOL
// |||| |CPHA
// |||| ||SPR1
// |||| |||SPR0
enum {// |||| ||||
spcr = 0b1100'0000 };
SPI spiSlave( spcr );// spi2x = 0
volatile uint16_t duration_us;
ISR(SPI_STC_vect){
duration_us = spiSlave.transfer<uint16_t>(0);
jet.pulseMicroseconds(duration_us);
}
int main(void) {
jet.output();
jet.write(0);
sei(); // enable interrupts
while (1) { }
return 0;
}
//==========
Спойлер
Код: Выделить всё
// Генериравание импульса заданной ширины в микросекундах.
// Прерывания отключены во время генерации импульса.
void pulseMicroseconds(uint16_t us){
if( us == 0 ){ return; }
uint8_t sreg = SREG; // F_CPU >= 1000000
__asm__ __volatile__("cli" ::: "memory"); //
sfr->pin |= MASK;
delayMicroseconds(us);
sfr->pin |= MASK;
SREG = sreg; //
__asm__ __volatile__("" ::: "memory"); //
};
Спойлер
Код: Выделить всё
// задерживает указанное количество микросекунд
// работает для тактовой частоты 1 MHz и выше
__attribute((always_inline))
static inline void delayMicroseconds(uint16_t us){
// if us is a compile-time constant result is accurate to 1 cycle
if (__builtin_constant_p(us)) {
_delay_us(us);
return;
}
// when us is not known at compile time, delay is accurate to +/- 2us
// plus an overhead of 3 CPU cycles
// когда us неизвестен во время компиляции, задержка составляет +/- 2us
// плюс накладные расходы в 3 цикла процессора
const float fMHz = (F_CPU/1000000.0);
// subtract two for rounding before dividing by 4
// вычтите два для округления перед делением на 4
us -= 2;
delay4us:
// delay 4us per loop, less 4 cycles for overhead
// задержка 4 мкс на цикл, минус 4 цикла накладных расходов
_delay_us(4.0 - (4.0 / fMHz));
asm volatile ("sbiw %[us], 4" : [us]"+w"(us));
asm goto( "brpl %l[delay4us]" :::: delay4us);
}
- Вложения
-
- Фрагмент-0.png
- (192.14 КБ) 131 скачивание
-
- Pulse.png
- (8.51 КБ) 141 скачивание
-
- SPI_Master_Slave.png
- (83.77 КБ) 122 скачивания
- Ivanoff-iv
- Друг Кота
- Сообщения: 7077
- Зарегистрирован: Пт ноя 11, 2016 05:48:09
- Откуда: Сердце Пармы
Re: Таймеры/счётчики в AVR
какова минимальная скважность работы форсунок?
(может ли наслаиваться открытие одной форсунки на открытие второй, третьей....)
(может ли наслаиваться открытие одной форсунки на открытие второй, третьей....)
Для тех, кто не учил магию мир полон физики 
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
Безграмотно вопрошающим про силовую или высоковольтную электронику я не отвечаю, а то ещё посадят за участие в (само)убиении оболтуса...
- Starichok51
- Модератор
- Сообщения: 19039
- Зарегистрирован: Сб авг 14, 2010 15:05:51
- Откуда: г. Озерск, Челябинская обл.
Re: Таймеры/счётчики в AVR
если не знаешь, как пишется слово, посмотри в словаре.Эйлер Леонард писал(а):инкриминирует
а инкриминировать можно кому-то, например, преступление.
Мудрость приходит вместе с импотенцией...
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
Когда на русском форуме переходят на Вы, в реальной жизни начинают бить морду.
- Just_Fluffy
- Вымогатель припоя
- Сообщения: 532
- Зарегистрирован: Ср июн 29, 2022 16:25:45
Re: Таймеры/счётчики в AVR
Я ни на какие вопросы отвечать не буду без присутствия моего авокадо!
Белая и Пушистая
- Эйлер Леонард
- Встал на лапы
- Сообщения: 104
- Зарегистрирован: Пн ноя 04, 2019 09:58:29
- Откуда: г. Нижний Тагил Свердл. обл.
Re: Таймеры/счётчики в AVR
Добрый вечер.
Решено. Использовал Timer/Counter2(8 bit) ATmega328P в режиме Clear Timer On Compare (CTC). Настроил прескалер и регистр сравнения OCR2A, F_CPU 16MHz. Обязательно необходимо установить разрешение, оно-же дискретность приращения для инкрементируемой переменной в обработчике прерывания TIMER2_COMPA_vect. При единице не работает. Нормальная обработка выполняется от 10. Установил 100 для надежности. Высокий уровень на лапке МК устанавливается в тот момент, когда приходит сигнал с датчика коленчатого вала INT1_vect. Там-же, в INT1_vect, фиксируется время каждого сигнала. Синхронизация сигналов выполняется по одиночноу сигналу от распредвала в прерывании INT0_vect. Низкий уровень на лапке МК устанавливается по истечении заданного интервала времени. Происходит сравнение разницы фиксированного и текущего времени возвращаемого функцией get_miсros() со значением uS для открытого состояния форсунки. Функции установки уровней (HIGH/LOW) вызываются по указателю из прерываний - внешнее прерывание INT1_vect (HIGH) и прерывание таймера TIMER2_COMPA_vect (LOW). Численное значение времени(uS) открытого состояния форсунки вычисляется другим МК(Master) и по готовности передается по SPI целевому МК(Slave).
Информации по теории и принципам работы распределенного впрыска ДВС в интернете очень мало. В основном только общее представление. В деталях и тонкостях приходится разбираться путем рассуждений. Например - могут ли все четыре форсунки находиться одновременно в открытом состоянии? Да, могут. Это происходит на режиме высоких оборотов (выше какого-то предела). Поскольку высокие обороты требуют большего количества топлива и, соответственно, большего времени открытого состояния. Но с высокими оборотами сокращается и время периода рабочего цикла. Часть топлива подается на закрытый клапан. Т.е. ШИМ 90-95%. Одновременное открытое состояние всех 4-х форсунок наступает при заполнении ШИМ более чем 25%. - четверть периода рабочего цикла. Что видно и на виртуальном осциллографе в протеусе.
Файл main.cpp
файл micros2.h
Решено. Использовал Timer/Counter2(8 bit) ATmega328P в режиме Clear Timer On Compare (CTC). Настроил прескалер и регистр сравнения OCR2A, F_CPU 16MHz. Обязательно необходимо установить разрешение, оно-же дискретность приращения для инкрементируемой переменной в обработчике прерывания TIMER2_COMPA_vect. При единице не работает. Нормальная обработка выполняется от 10. Установил 100 для надежности. Высокий уровень на лапке МК устанавливается в тот момент, когда приходит сигнал с датчика коленчатого вала INT1_vect. Там-же, в INT1_vect, фиксируется время каждого сигнала. Синхронизация сигналов выполняется по одиночноу сигналу от распредвала в прерывании INT0_vect. Низкий уровень на лапке МК устанавливается по истечении заданного интервала времени. Происходит сравнение разницы фиксированного и текущего времени возвращаемого функцией get_miсros() со значением uS для открытого состояния форсунки. Функции установки уровней (HIGH/LOW) вызываются по указателю из прерываний - внешнее прерывание INT1_vect (HIGH) и прерывание таймера TIMER2_COMPA_vect (LOW). Численное значение времени(uS) открытого состояния форсунки вычисляется другим МК(Master) и по готовности передается по SPI целевому МК(Slave).
Информации по теории и принципам работы распределенного впрыска ДВС в интернете очень мало. В основном только общее представление. В деталях и тонкостях приходится разбираться путем рассуждений. Например - могут ли все четыре форсунки находиться одновременно в открытом состоянии? Да, могут. Это происходит на режиме высоких оборотов (выше какого-то предела). Поскольку высокие обороты требуют большего количества топлива и, соответственно, большего времени открытого состояния. Но с высокими оборотами сокращается и время периода рабочего цикла. Часть топлива подается на закрытый клапан. Т.е. ШИМ 90-95%. Одновременное открытое состояние всех 4-х форсунок наступает при заполнении ШИМ более чем 25%. - четверть периода рабочего цикла. Что видно и на виртуальном осциллографе в протеусе.
Файл main.cpp
Спойлер
Код: Выделить всё
/* ATtiny88 AtmelStudio -std=c++14 */
#define F_CPU 16000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include "D:\lib\MegaCore\corefiles\HardwareSerial.h"
#include "D:\lib\SPI\SPI.hpp"
#include "micros2.h"
#include "D:\lib\pin\GPIO.hpp"
GPIO<PIN::PinC0> jet0;
GPIO<PIN::PinC1> jet1;
GPIO<PIN::PinC2> jet2;
GPIO<PIN::PinC3> jet3;
GPIO<PIN::PinD2> INT_0;
GPIO<PIN::PinD3> INT_1;
void INT1_interrupt_attach( void (*funcPtr)(void) );
void (*INT1_interruptCallback)(void);
static void positionCheck(void);
static void Action_4Jet(uint16_t d);
volatile uint16_t duration_us = 0;//8000
volatile uint32_t lastTime0, lastTime1, lastTime2, lastTime3;
ISR(INT0_vect) {
GPIOR0 = 0;
}
ISR(INT1_vect) {
if(!!duration_us){
INT1_interruptCallback();
}
GPIOR0++;
}
// SPIE
// |SPE
// ||DORD
// |||MSTR
// |||| CPOL
// |||| |CPHA
// |||| ||SPR1
// |||| |||SPR0
enum {// |||| ||||
spcr = 0b1100'0000 };
SPI spiSlave( spcr );// spi2x = 0
ISR(SPI_STC_vect){
duration_us = spiSlave.transfer<uint16_t>(0);
Serial.println(duration_us);
}
// ISR Handlers
ISR(TIMER2_COMPA_vect) {
microseconds += micros_resolution;
if(interruptCallback){
interruptCallback(duration_us);
}
}
int main(void){
Serial.begin(57600);//
while(!Serial);
Serial.println("== TEST Slave ==");
jet0.output(); jet0.write(0);
jet1.output(); jet1.write(0);
jet2.output(); jet2.write(0);
jet3.output(); jet3.write(0);
INT_0.input(); INT_0.pullup();
INT_1.input(); INT_1.pullup();
EIMSK |= (1<<INT1)|(1<<INT0); // Разрешение прерывания на входе INT0, INT1
EICRA |= (1<<ISC11)|(1<<ISC10)|(1<<ISC01)|(1<<ISC00); // Нарастающий фронт INT1, INT0 генерирует прерывание.
INT1_interrupt_attach(positionCheck);
micros_interrupt_attach(Action_4Jet);
micros_init(F_CPU, 100);
while(1){ }
return 0;
}
// ==
static void Action_4Jet( uint16_t d_us ){
if( (micros_get() - lastTime0) >= d_us ){
lastTime0 = micros_get();
jet0.write(0);
}
if( (micros_get() - lastTime1) >= d_us ){
lastTime1 = micros_get();
jet1.write(0);
}
if( (micros_get() - lastTime2) >= d_us ){
lastTime2 = micros_get();
jet2.write(0);
}
if( (micros_get() - lastTime3) >= d_us ){
lastTime3 = micros_get();
jet3.write(0);
}
}
// ==
static void positionCheck(void){
switch(GPIOR0) {
case 0: {
lastTime0 = micros_get();
jet0.write(1);
break;}
case 1: {
lastTime1 = micros_get();
jet1.write(1);
break;}
case 2: {
lastTime2 = micros_get();
jet2.write(1);
break;}
case 3: {
lastTime3 = micros_get();
jet3.write(1);
break;}
}
}
// ==
void INT1_interrupt_attach( void (*funcPtr)(void) ){
INT1_interruptCallback = funcPtr;
}
Спойлер
Код: Выделить всё
#ifndef MICROSJET_H
#define MICROSJET_H
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/power.h>
#include <util/atomic.h>
// Timer2
#if F_CPU > 1000000 // 1MHz - 20MHz
#define CLOCKSEL (1 << CS21)
#define MICROS_PRESCALER 8
#elif F_CPU >= 125000 // 125KHz - 1MHz
#define CLOCKSEL (1 << CS20)
#define MICROS_PRESCALER 1
#else
#error "F_CPU out of range."
#endif
void micros_init(uint32_t f_cpu, uint16_t resolution);
uint32_t micros_get(void);
void micros_resume(void);
void micros_pause(void);
void micros_reset(void);
void micros_add(uint32_t us);
void micros_subtract(uint32_t us);
void micros_interrupt_attach( void (*funcPtr)(uint16_t) );
// ============ implementation ============
// GLOBAL VARIABLES
// Ошибка калибровки. Отрицательное или положительное число
// которое будет добавлено или вычтено из значения регистра таймера.
enum err { MICROS_ERR_CAL = 0 };
// тип (*имя_указателя)(типы_параметров);
void (*interruptCallback)(uint16_t);
volatile uint32_t microseconds;
static uint16_t micros_resolution;
void micros_init(uint32_t f_cpu, uint16_t resolution ){// Timer settings
micros_resolution = resolution; // разрешение
// Clear registers
TCCR2A = 0;
TCCR2B = 0;
TCNT2 = 0;
TCCR2A |= (1 << WGM21); // CTC
TCCR2B |= CLOCKSEL; // Prescaler 8
TIMSK2 |= (1 << OCIE2A); // Output Compare Match A Interrupt Enable
OCR2A = (f_cpu / MICROS_PRESCALER / (1000000 / resolution)) + MICROS_ERR_CAL - 1;
sei(); // enable Global Interrupts
}
// Returns current microseconds
// Возвращает текущие миллисекунды
uint32_t micros_get(){
uint32_t ms;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
ms = microseconds;
}
return ms;
}
// Turn on timer and resume time keeping
// Включите таймер и возобновите отсчет времени
void micros_resume(){
TIMSK2 |= (1 << OCIE2A);
TCCR2B = CLOCKSEL;
power_timer2_enable();
}
// Pause time keeping and turn off timer to save power
// Приостановите отсчет времени и выключите таймер для экономии энергии
void micros_pause(){
TIMSK2 &= ~(1 << OCIE2A);
TCCR2B = 0;
power_timer2_disable();
}
// Reset microseconds count to 0
// Сбросить счетчик миллисекунд до 0
void micros_reset(){
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
microseconds = 0;
}
}
// Add time
// Добавить время
void micros_add(uint32_t ms){
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
microseconds += ms;
}
}
// Subtract time
// Вычесть время
void micros_subtract(uint32_t ms){
ATOMIC_BLOCK(ATOMIC_RESTORESTATE){
microseconds -= ms;
}
}
// Attach a custom function to timer interrupt
// Прикрепите пользовательскую функцию к прерыванию таймера
void micros_interrupt_attach( void (*funcPtr)(uint16_t) ){
interruptCallback = funcPtr;
}
#endif // MICROSJET_H- Вложения
-
- Test-1.png
- (12.84 КБ) 116 скачиваний
-
- Test-0.png
- (83.61 КБ) 113 скачиваний
