верно? скваженостью рулить отдельно через значения OCR0A и OCR0B?
Если мы Не хотим шим на выводы а хотим управлять вручную через процедуры - настраиваем TIMSK0, тогда ноги освобождаются и управление передается в процедуры..
Пока верно? Спасибо большое!
Несмотря на то, что у 45 тини два таймера, цель - помимо разобраться в целом - перейти на 13 тини, поэтому буду даже на 45 наседать на таймер 0. Осталось найти замену millis() ))))
fast PWM с уровнем LOW между значениями OCR0A/OCR0B и 255. С помощью COMxN[0:1] можно еще два варианта дергания ногами настроить.
Цитата:
скваженостью рулить отдельно через значения OCR0A и OCR0B?
да
Цитата:
Если мы Не хотим шим на выводы а хотим управлять вручную через процедуры - настраиваем TIMSK0, тогда ноги освобождаются и управление передается в процедуры..
Одно другому не мешает - можно и выводы дергать (ненулевые значения в COMxN[0:1]) и нужные прерывания подключить (через соотв. биты TIMSKx)
Цитата:
Пока верно? Спасибо большое!
Несмотря на то, что у 45 тини два таймера, цель - помимо разобраться в целом - перейти на 13 тини, поэтому буду даже на 45 наседать на таймер 0. Осталось найти замену millis() ))))
Заменой millis может стать подключение прерывания по переполнению таймера/счетчика (ISR(TIMER0_OVF_vect) {}), которое будет вызываться каждые 256/1/8000000 сек = 32 микросекунды.
Инкрементируем внутри процедуры обработки прерывания по переполнению счетчик и когда он достигнет значения 31, сбрасываем его и увеличиваем на 1 счетчик миллисекунд. Получится чуть-чуть меньше миллисекунды (на 8 микросекунд), но и эту погрешность можно устранить, раз в 4 миллисекунды совершая сброс/инкремент не на 31-м, а на 32-м прерывании.
fast PWM с уровнем LOW между значениями OCR0A/OCR0B и 255. С помощью COMxN[0:1] можно еще два варианта дергания ногами настроить
Вот этот момент так и не могу понять.. Правильно я понимаю - биты COM0A[0:1] управляют выводом ШИМа на PB0 и его "типом", а COM0B[0:1] его выводом и типом на PB1?
остальное понял, замечательно что можно одновременно аппаратно дергать ногами и юзать программно таймер!
Для упрощения процесса (на переходной стадии между ардуино и винавр) - millis() при настройках таймера в fastpwm продолжит работу, просто будет "врать" при настройке предделителя отличном от стандарта?
Кстати, частота 45 тини 8мгц (устраиваемая меня), у 13-й же 9,6 мгц. Надо не забыть при пересчете )
step962, я уже вовсю переписываю программу в winavr. ШИМ как Вы поняли уже запустил, сейчас заканчиваю пробник расчета времени (аналог миллис). Правильно я рассчитываю?
unsigned short tcounter = 0; //счетчик таймера, используется для вычисления задержек времени
unsigned long millis = 0; //счетчик прошедших миллисекунд с начала работы
unsigned long delay_millis = 0; //переменная для сохранения значения millis и последующего сравнения
..
// отслеживаем прерывания по переполнению (TOIE2)
TIMSK |= (1<<TOIE0);
..
void ISR(int TIMER0_COMPA_vect) {
tcounter++;
//обработка счетчика таймера для расчета времени
if(tcounter == 4) { //для частоты 3906гц, каждые 3906 / 1000 такта увеличиваем микросекунду
tcounter = 0;
millis++;
}
return;
}
..
Проверка, если вышеприведенное верно, не будет отличаться особо от работы с millis();
Все зависит от той точности, которую вы хотите получить. Если погрешность в 2% не страшна, то можно оставить и так. Нет - придется дополнительные проверки вводить и, например, при каждом 50-м вызове прерывания сброс счетчика tcounter производить при достижении значения 3, а не 4.
И да, ISR определяется совсем без типа - даже без void.
Все зависит от той точности, которую вы хотите получить. Если погрешность в 2% не страшна, то можно оставить и так. Нет - придется дополнительные проверки вводить и, например, при каждом 50-м вызове прерывания сброс счетчика tcounter производить при достижении значения 3, а не 4.
И да, ISR определяется совсем без типа - даже без void.
Погрешность даже в 10% была бы не страшна) Все задержки - "на глаз" )
ISR без инт немного ругается варнингом) (говорит что по дефолту там инт, и я его поставлю туды)
Ой, просмотрел - в ISR ни в коем случае return нельзя делать - механизм вызова прерываний и возврата из них отличается от принятого в функциях. Поэтому и компилятор ругается: видит return, не видит возвращаемого типа ну и обижается...
И перед TIMER0_COMPA_vect определение типа не требуется - это константа (номер вектора прерывания), а не параметр.
Понял, поправил, да и из других своих процедур поубирал return, если им возвращать нечего. Тут вылезло что то странное - при записи в OCR0A и OCR0B значений, например, 200 - ШИМ пропадает на МК, устанавливается высокий уровень на пинах..
Проверяю сейчас все.
----
UPD нашел. При записи 255 его "заносит" на полную, на 254 все ОК )
Правда там "самый маленький камень" который можно выбрать ATinny25, у которого 2048 byte maximum. Но возможно, что и Atinny13 не так уж сильно отличается, и добавить его поддержку будет не трудно (особенно после столь глубого изучения даташитов). А возможно и просто "залить" в него прошивку для ATinny25 получится. Может они только объемом памяти отличаются.
Дело в том, что скетч вырос уже раза в 4 с того времени) Меньше 1140 помоему в ардуино не удавалось его сделать. Но то полбеды - никак не смог запустить адруино и 13 тини - не находил в сети пинмаппинга..
Да и все таки я думаю.. пора. Потихоньку, полегоньку..
Дело в том, что скетч вырос уже раза в 4 с того времени)
Но ведь его рост, в большой степени вызван тем, что реализуете те функции которые уже есть в библиотеках. Руками.
Использование библиотек подразумевает что скетч "опять должен сократится". Уменьшится количество кода которое "вы написали".
whoim пишет:
Меньше 1140 помоему в ардуино не удавалось его сделать.
Размер зависит от того под какую плату компилите. У меня, ваш скетч, под Mega1280 компилится в 1536bytes, а он же под Atiny25 в 672bytes.
whoim пишет:
Но то полбеды - никак не смог запустить адруино и 13 тини - не находил в сети пинмаппинга..
Ну сейчас думаю уже проблем с тем "какая нога что значит" - нет. К тому же этот "пинмаппинг" вы сами можете задать, как вам хочется в файле pins_arduino.h для вашей платы.
whoim пишет:
Да и все таки я думаю.. пора. Потихоньку, полегоньку..
Ну с этим трудно поспорить. Сказать что "вы зря потратили время на разбирательство" - может только глупый человек. Стратегически, конечно, ваш путь более правильный. Однозначно.
Я просто предложил "альтернативный вариант". Сейчас на него сворачивать, безусловно, смысла нет. Но IMHO "держать в голове" - стоит. Вдруг в каком-то следующем проекте потребуется что-то "накидать по быстрому". Думаю так будет все-таки быстрее, чем "вручную" все конфигурировать, если нужно "всего-лишь подергать ногой", или что-то подобное.
Про "более правильный путь" и я согласен. Но недавно выслушивал человека, который не видит никакой нужды "знать" как оно работает "изнутри" - и размер кода его мало волнует. В общем, та же тенденция, что и на PC - увеличение "всего" в железе тут же отражается на размерах программ и стилях программирования ))
И я не говорю что тот путь правилен или этот. Для разных задач и разных целей..
Сейчас моя первая цель - разобраться. Знать "изнутри". Вот, например, и компромисс - я попробовал библиотеку delay.h и выяснил, что строчка _delay_ms(15) решает мою задачу без увеличения размера прошивки. Так почему нет? ))
//обработка счетчика таймера для расчета времени if(tcounter == 4) { //для частоты 3906гц, каждые 3906 / 1000 такта увеличиваем микросекунду tcounter = 0; millis++; } }
А как millis и tcounter объявлены? volatile не забыли? Иначе вы можете только думать что это одна и таже переменная (внутри обработчика и снаружи), а компилятор-оптимизатор мог раскидать по разным адресам памяти ее.
Про "более правильный путь" и я согласен. Но недавно выслушивал человека, который не видит никакой нужды "знать" как оно работает "изнутри" - и размер кода его мало волнует. В общем, та же тенденция, что и на PC - увеличение "всего" в железе тут же отражается на размерах программ и стилях программирования ))
И я не говорю что тот путь правилен или этот. Для разных задач и разных целей..
Согласен. "Для разных задачь". Но сейчас, "плевать на размер" - более часто подходит чем наоборот. Чисто из экономических соображений. Стоимость квалификации "который знает изнутри" - выше. Скорость разработки (опять-таки деньги и сроки) и т.д. и т.п. Добавить лишний гигабайт памяти, нынче, дешевле чем оплатить час работы программера. И результат "сразу на руках" :)
Я вот тоже, вначале пытался "жатся в маленькие камни", а потом плюнул начал брать только "пожирнее". Разница в цене, совершенно не соотвествует времени затраченного на оптимизацию.
Ну, в моем случае аттини13 ~ 40руб, аттини45 ~175 руб. Цена готового устройства около 800 руб (если найдутся желающие конечно). Ну как бы.. можно и потрудится, тем более что "для сэбэ" ведь )
Ну, в моем случае аттини13 ~ 40руб, аттини45 ~175 руб. Цена готового устройства около 800 руб (если найдутся желающие конечно). Ну как бы.. можно и потрудится, тем более что "для сэбэ" ведь )
Ну вот я про это и говорю. Разница 135руб. То есть <5$. Если взять "стоимость часа спеца в $50", то это разница "меньше минуты его работы". Если он провазявкается с портированием хотя-бы неделю, то страшно представить сколько конечных устройств нужно продать что-бы окупить этот переход.
Но даже если вы "не спец" и ваше время не стоит $50. Но, как-бы то нибыло, не думаю что ваше время стоит меньше $5 час.
Так что, выходит, чисто экономически это абсолютно не целесообразно. Только как "учеба" или "just for fun". "Выгодно" это может стать только при крупносерийном произвостве. Когда каждый цент умножается на десятки и сотни тысяч (и то, "сроки разработки" могут заставить выбрать более дорогой вариант).
Не заметил в приведенных вами кодах вызова функции timer_on().
И строку
void ISR (TIMER0_OVF_vect) {
стоит все же переписать в виде
ISR (TIMER0_OVF_vect) {
после удаления return куомпилятор вроде не должен ругаться. Или ворчит-таки?
И еще:
везде, где у вас используется присваивание "|=", посмотрите, а стоит ли сохранять уже установленные биты? Вот в TCCR0A, например, точно стоит все неустанавливаемые биты обнулить:
или по крайней мере посмотреть в даташите, а не инициализируются ли они (неустанавливаемые биты) единичками? Ведь вы же хотите в этой инструкции выбрать для каналов именно режим вывода 2, а никак не 3?
Вообще, |= и &= имеет смысл использовать, если у вас соответствующий байт/порт/регистр и в других местах изменяется и вы хотите целенаправленно "подправить" один-два бита в байте/порте/регистре. Или "набираете" нужную конфигурацию в нескольких операторах. Или используете библиотеку, которая что-то там тоже устанавливает.
Не заметил в приведенных вами кодах вызова функции timer_on().
И строку
void ISR (TIMER0_OVF_vect) {
стоит все же переписать в виде
ISR (TIMER0_OVF_vect) {
после удаления return куомпилятор вроде не должен ругаться. Или ворчит-таки?
И еще:
везде, где у вас используется присваивание "|=", посмотрите, а стоит ли сохранять уже установленные биты? Вот в TCCR0A, например, точно стоит все неустанавливаемые биты обнулить:
или по крайней мере посмотреть в даташите, а не инициализируются ли они (неустанавливаемые биты) единичками? Ведь вы же хотите в этой инструкции выбрать для каналов именно режим вывода 2, а никак не 3?
Вообще, |= и &= имеет смысл использовать, если у вас соответствующий байт/порт/регистр и в других местах изменяется и вы хотите целенаправленно "подправить" один-два бита в байте/порте/регистре. Или "набираете" нужную конфигурацию в нескольких операторах. Или используете библиотеку, которая что-то там тоже устанавливает.
timer_on есть, я подразумевал что он будет)
void уже убрал! Завелось все, все работает. Уже не помню, кажется на sie ругался и я его закоментил, а надо было asm("sei"); использовать
Напомню цель: управление PWM мощными лампами (ближний либо дальний свет авто), сигнал Разрешено (зажигание), Запрет (ближний/дальний/аварийный даитчик масла/ручник, все через диоды), Моргать (по раздельности фарами, ака жеймсбонд)
#define F_CPU 8000000UL // 8 MHz
#include <avr/io.h>
#include <avr/delay.h>
#include <avr/interrupt.h>
//логические
#define true 1
#define false 0
//пины МК
#define LAMP1 PB0 //лампа 1
#define LAMP2 PB1 //лампа 2
#define BLINK PB2 //вход мигания
#define ACC PB3 //вход зажигания
#define BAD PB4 //входы запрета
//остальное
#define MAX 204 //максимальный уровень ШИМ (0..255) (204 = 80%)
#define OFF_DELAY 980 //задержка перед выключением, (8000мс желаемая задержка / (1000мс /122,5гц текущей частоты таймера))
//переменные
volatile unsigned char state = false; //состояние ламп
volatile unsigned short tcounter = 0; //счетчик таймера, используется для вычисления задержек времени
volatile unsigned long millis = 0; //счетчик прошедших миллисекунд с начала работы
volatile unsigned long delay_millis_1 = 0; //переменная для сохранения значения millis и последующего сравнения
volatile unsigned long delay_millis_2 = 0; //переменная для сохранения значения millis и последующего сравнения
volatile unsigned long off_time = 0; //переменная для сохранения значения millis и последующего сравнения
volatile unsigned char off_flag = false; //флаг задержки выключения
//Вспомогательные функции и процедуры
void timer_on(void) {
//PWM Settings
TCCR0A = ((1 << WGM01)|(1 << WGM00)); // non inverting fast pwm, OC0A + OC0B pins потом
//выбор предделителя
//TCCR0B = (1 << CS00); //делитель 0, частота 8000000/255=31372гц
//TCCR0B = (1 << CS01); //делитель 8, частота 8000000/8/255=3906,5гц
//TCCR0B = ((1 << CS01)|(1 << CS00)); //делитель 64, частота 8000000/64/255=490,19гц
TCCR0B = (1 << CS02); //делитель 256, частота 8000000/256/255=122,5гц
//TCCR0B = ((1 << CS02)|(1 << CS00)); //делитель 1024, частота 8000000/1024/255=30,6гц
// отслеживаем прерывания по переполнению (TOIE2)
TIMSK = (1<<OCIE0A);
//скважность каналов 0
OCR0A = 0;
OCR0B = 0;
asm("sei"); //разрешить прерывания
state=false; //флаг в выключено
}
//Отслеживаем переполнение
ISR (TIMER0_COMPA_vect) {
/*tcounter++;
//обработка счетчика таймера для расчета времени
if(tcounter == 31) { //для частоты 31372гц, каждые 31372гц / 1000 такта увеличиваем микросекунду
tcounter = 0;
millis++;
}
*/ //отключаем, на частоте ниже 1000 гц не имеет смысла, заменяем на
millis++; //с расчетом и исправлением значений задержек
}
void lamp_on(void) {
//включаем пины ШИМ
TCCR0A |= ((1 << COM0A1)|(1 << COM0B1));
//скачок для старта (для мощных ламп)
OCR0A = 254;
OCR0B = 254;
_delay_ms(30);
OCR0A = 0;
OCR0B = 0;
for (int x=0; x <= MAX; x++) {
OCR0A = x;
OCR0B = x;
//организация задержки
_delay_ms(15);
}
state=true; //устанавливаем признак - лампы включены
}
void lamp_off(void) {
for (int x=MAX; x >= 0; x--) {
OCR0A = x;
OCR0B = x;
//организация задержки
_delay_ms(15);
}
state=false; //устанавливаем признак - лампы выключены
//выключаем пины ШИМ
TCCR0A ^= ((1 << COM0A1)|(1 << COM0B1));
}
//моргать
void blink(void) {
//на всякий включаем пины ШИМ
TCCR0A |= ((1 << COM0A1)|(1 << COM0B1));
//выключить все
OCR0A = 0;
OCR0B = 0;
_delay_ms(200);
//моргаем первой лампой два раза
OCR0A = 254;
_delay_ms(80);
OCR0A = 0;
_delay_ms(80);
OCR0A = 254;
_delay_ms(80);
OCR0A = 0;
_delay_ms(400); //пауза после первой фары
//моргаем второй фарой два раза
OCR0B = 254;
_delay_ms(80);
OCR0B = 0;
_delay_ms(80);
OCR0B = 254;
_delay_ms(80);
OCR0B = 0;
_delay_ms(400); //пауза после второй фары
state = false; //флаг, так как все выключено
//выключаем пины ШИМ
TCCR0A ^= ((1 << COM0A1)|(1 << COM0B1));
}
//Главное тело программы
int main(void) {
//настройки
DDRB = 0b00000011;
PORTB = 0b00011100;
timer_on();
//главный цикл
while(1) {
//если пришло время проверки
if (delay_millis_1 < millis - 100) {
delay_millis_1 = millis;
//если выключено, есть разрешение и нет запрета - включить
if (state==false && !(PINB&(1<<ACC)) && (PINB&(1<<BAD))) {
lamp_on();
}
//если включено и позволяют сигналы - выключить
if (state) {
if ((PINB&(1<<ACC)) || !(PINB&(1<<BAD))) {
if (!(PINB&(1<<BAD))) { //если пришел сигнал по линии запрета выключаем сразу
OCR0A = 0;
OCR0B = 0;
//выключаем пины ШИМ
TCCR0A ^= ((1 << COM0A1)|(1 << COM0B1));
state = false;
}
else {
//высчитываем задержки
if (off_flag == false) {
off_time = millis;
off_flag = true;
}
if (off_time < millis - OFF_DELAY) {
off_flag = false;
lamp_off();
}
}
} //if PINB
} //if выключить
//пока сигнал "моргать"
while(!(PINB&(1<<BLINK))) {
blink();
}//моргать
} //if время проверки
asm volatile ("nop"); //бездельничаем
} //while(1)
} //main()
Program: 732 bytes
--------
UDP текст программы - выключение ШИМа на пинах когда он не нужен. Лампочка не поет, нет ощущения что сидишь в трансформаторной будке)
К сожалению, на реальном устройстве на частоте 4кгц уже нагреваются мосфеты, на 30 они неуправляемы, несмотря на раскачу двумя плечами. Без драйвера видимо не обойтись если хочешь около 30 кгц.
Есть желание попробовать тот самый режим с "коррекцией фазы" и 15кгц, но наверно чуть позже пристану :)
Есть желание попробовать тот самый режим с "коррекцией фазы" и 15кгц, но наверно чуть позже пристану :)
При тех же остальных настройках вместо режима "3" выбрать "1". Частота автоматически уменьшается в два раза (за счет счета ;) счетчика :)) в обе стороны)
Ага, чтобы включить fastpwm на pb0 и pb1, мне нужно
верно? скваженостью рулить отдельно через значения OCR0A и OCR0B?
Если мы Не хотим шим на выводы а хотим управлять вручную через процедуры - настраиваем TIMSK0, тогда ноги освобождаются и управление передается в процедуры..
Пока верно? Спасибо большое!
Несмотря на то, что у 45 тини два таймера, цель - помимо разобраться в целом - перейти на 13 тини, поэтому буду даже на 45 наседать на таймер 0. Осталось найти замену millis() ))))
Ага, чтобы включить fastpwm на pb0 и pb1, мне нужно
верно?
fast PWM с уровнем LOW между значениями OCR0A/OCR0B и 255. С помощью COMxN[0:1] можно еще два варианта дергания ногами настроить.
скваженостью рулить отдельно через значения OCR0A и OCR0B?
да
Если мы Не хотим шим на выводы а хотим управлять вручную через процедуры - настраиваем TIMSK0, тогда ноги освобождаются и управление передается в процедуры..
Одно другому не мешает - можно и выводы дергать (ненулевые значения в COMxN[0:1]) и нужные прерывания подключить (через соотв. биты TIMSKx)
Пока верно? Спасибо большое!
Несмотря на то, что у 45 тини два таймера, цель - помимо разобраться в целом - перейти на 13 тини, поэтому буду даже на 45 наседать на таймер 0. Осталось найти замену millis() ))))
Заменой millis может стать подключение прерывания по переполнению таймера/счетчика (ISR(TIMER0_OVF_vect) {}), которое будет вызываться каждые 256/1/8000000 сек = 32 микросекунды.
Инкрементируем внутри процедуры обработки прерывания по переполнению счетчик и когда он достигнет значения 31, сбрасываем его и увеличиваем на 1 счетчик миллисекунд. Получится чуть-чуть меньше миллисекунды (на 8 микросекунд), но и эту погрешность можно устранить, раз в 4 миллисекунды совершая сброс/инкремент не на 31-м, а на 32-м прерывании.
fast PWM с уровнем LOW между значениями OCR0A/OCR0B и 255. С помощью COMxN[0:1] можно еще два варианта дергания ногами настроить
Вот этот момент так и не могу понять.. Правильно я понимаю - биты COM0A[0:1] управляют выводом ШИМа на PB0 и его "типом", а COM0B[0:1] его выводом и типом на PB1?
остальное понял, замечательно что можно одновременно аппаратно дергать ногами и юзать программно таймер!
Правильно.
[quote=whoim]
замечательно что можно одновременно аппаратно дергать ногами и юзать программно таймер!
[/qoute]
Что Arduino и делает, руля часами (прерывания в таймере0) и предоставляя 6 каналов ШИМ (по два на каждый из таймеров 0,1,2) одновременно.
Для упрощения процесса (на переходной стадии между ардуино и винавр) - millis() при настройках таймера в fastpwm продолжит работу, просто будет "врать" при настройке предделителя отличном от стандарта?
Кстати, частота 45 тини 8мгц (устраиваемая меня), у 13-й же 9,6 мгц. Надо не забыть при пересчете )
Ну что ж, если верить протеусу - частота не превышает 490гц при любых настройках предделителя. При компилировании в Ардуино.
Полезу ставить винавр..
Да, в винавр все четко
Ну что ж, если верить протеусу - частота не превышает 490гц при любых настройках предделителя. При компилировании в Ардуино.
Ну так 8000000/64/256=488,3 Гц.
Вполне возможно, ардуиновские настройки таймеров выполняются после пользовательских (кто его знает, куда компилятор сует соответствующие коды).
Здесь можно попытаться обмануть компилятор, сунув коды настройки в loop и выполнив их там один раз при первом проходе, а потом обходить.
step962, я уже вовсю переписываю программу в winavr. ШИМ как Вы поняли уже запустил, сейчас заканчиваю пробник расчета времени (аналог миллис). Правильно я рассчитываю?
Проверка, если вышеприведенное верно, не будет отличаться особо от работы с millis();
Здесь можно попытаться обмануть компилятор, сунув коды настройки в loop и выполнив их там один раз при первом проходе, а потом обходить.
Не получится.
Между setup() и loop() вообщем-то ничего особого не происходит
arduino-1.0\hardware\arduino\cores\arduino\main.cpp
Все зависит от той точности, которую вы хотите получить. Если погрешность в 2% не страшна, то можно оставить и так. Нет - придется дополнительные проверки вводить и, например, при каждом 50-м вызове прерывания сброс счетчика tcounter производить при достижении значения 3, а не 4.
И да, ISR определяется совсем без типа - даже без void.
Все зависит от той точности, которую вы хотите получить. Если погрешность в 2% не страшна, то можно оставить и так. Нет - придется дополнительные проверки вводить и, например, при каждом 50-м вызове прерывания сброс счетчика tcounter производить при достижении значения 3, а не 4.
И да, ISR определяется совсем без типа - даже без void.
Погрешность даже в 10% была бы не страшна) Все задержки - "на глаз" )
ISR без инт немного ругается варнингом) (говорит что по дефолту там инт, и я его поставлю туды)
Ой, просмотрел - в ISR ни в коем случае return нельзя делать - механизм вызова прерываний и возврата из них отличается от принятого в функциях. Поэтому и компилятор ругается: видит return, не видит возвращаемого типа ну и обижается...
И перед TIMER0_COMPA_vect определение типа не требуется - это константа (номер вектора прерывания), а не параметр.
Сделайте так:
Понял, поправил, да и из других своих процедур поубирал return, если им возвращать нечего. Тут вылезло что то странное - при записи в OCR0A и OCR0B значений, например, 200 - ШИМ пропадает на МК, устанавливается высокий уровень на пинах..
Проверяю сейчас все.
----
UPD нашел. При записи 255 его "заносит" на полную, на 254 все ОК )
step962, я уже вовсю переписываю программу в winavr.
Нифига себе сходил за хлебушком. :) Хотя конечно, с точки зрения образования, ветка получилась зело интерестная.
Попробовал скомпилить ваш скетч из первого поста с помощью code.google.com/p/arduino-tiny/
Вышло "Binary sketch size: 672 bytes".
Правда там "самый маленький камень" который можно выбрать ATinny25, у которого 2048 byte maximum. Но возможно, что и Atinny13 не так уж сильно отличается, и добавить его поддержку будет не трудно (особенно после столь глубого изучения даташитов). А возможно и просто "залить" в него прошивку для ATinny25 получится. Может они только объемом памяти отличаются.
Дело в том, что скетч вырос уже раза в 4 с того времени) Меньше 1140 помоему в ардуино не удавалось его сделать. Но то полбеды - никак не смог запустить адруино и 13 тини - не находил в сети пинмаппинга..
Да и все таки я думаю.. пора. Потихоньку, полегоньку..
Естественно.
у таймера при этом (OCRxN=255) оба события - прерывание по сравнению и прерывание по переполнению счетчика - происходят одновременно.
Что то затык у меня на этом..
ничонепонимаю - убираю это - работает. Похоже, вечный цикл получился..
Дело в том, что скетч вырос уже раза в 4 с того времени)
Но ведь его рост, в большой степени вызван тем, что реализуете те функции которые уже есть в библиотеках. Руками.
Использование библиотек подразумевает что скетч "опять должен сократится". Уменьшится количество кода которое "вы написали".
Меньше 1140 помоему в ардуино не удавалось его сделать.
Размер зависит от того под какую плату компилите. У меня, ваш скетч, под Mega1280 компилится в 1536bytes, а он же под Atiny25 в 672bytes.
Но то полбеды - никак не смог запустить адруино и 13 тини - не находил в сети пинмаппинга..
Ну сейчас думаю уже проблем с тем "какая нога что значит" - нет. К тому же этот "пинмаппинг" вы сами можете задать, как вам хочется в файле pins_arduino.h для вашей платы.
Да и все таки я думаю.. пора. Потихоньку, полегоньку..
Ну с этим трудно поспорить. Сказать что "вы зря потратили время на разбирательство" - может только глупый человек. Стратегически, конечно, ваш путь более правильный. Однозначно.
Я просто предложил "альтернативный вариант". Сейчас на него сворачивать, безусловно, смысла нет. Но IMHO "держать в голове" - стоит. Вдруг в каком-то следующем проекте потребуется что-то "накидать по быстрому". Думаю так будет все-таки быстрее, чем "вручную" все конфигурировать, если нужно "всего-лишь подергать ногой", или что-то подобное.
Что то затык у меня на этом..
ничонепонимаю - убираю это - работает. Похоже, вечный цикл получился..
А что такое millis, тут? Похоже на переменную, а не вызов функции. Не и, естественно, раз она не меняется внутри цикла - цикл вечен.
Про "более правильный путь" и я согласен. Но недавно выслушивал человека, который не видит никакой нужды "знать" как оно работает "изнутри" - и размер кода его мало волнует. В общем, та же тенденция, что и на PC - увеличение "всего" в железе тут же отражается на размерах программ и стилях программирования ))
И я не говорю что тот путь правилен или этот. Для разных задач и разных целей..
Сейчас моя первая цель - разобраться. Знать "изнутри". Вот, например, и компромисс - я попробовал библиотеку delay.h и выяснил, что строчка _delay_ms(15) решает мою задачу без увеличения размера прошивки. Так почему нет? ))
Что то затык у меня на этом..
ничонепонимаю - убираю это - работает. Похоже, вечный цикл получился..
А что такое millis, тут? Похоже на переменную, а не вызов функции. Не и, естественно, раз она не меняется внутри цикла - цикл вечен.
//обработка счетчика таймера для расчета времени if(tcounter == 4) { //для частоты 3906гц, каждые 3906 / 1000 такта увеличиваем микросекунду tcounter = 0; millis++; } }
А как millis и tcounter объявлены? volatile не забыли? Иначе вы можете только думать что это одна и таже переменная (внутри обработчика и снаружи), а компилятор-оптимизатор мог раскидать по разным адресам памяти ее.
Забыл) Сейчас проверю
---
UPD нет, не помогает. Да ладно, _delay_ms работает, позже изучу либу, посмотрю как сделано.
Про "более правильный путь" и я согласен. Но недавно выслушивал человека, который не видит никакой нужды "знать" как оно работает "изнутри" - и размер кода его мало волнует. В общем, та же тенденция, что и на PC - увеличение "всего" в железе тут же отражается на размерах программ и стилях программирования ))
И я не говорю что тот путь правилен или этот. Для разных задач и разных целей..
Согласен. "Для разных задачь". Но сейчас, "плевать на размер" - более часто подходит чем наоборот. Чисто из экономических соображений. Стоимость квалификации "который знает изнутри" - выше. Скорость разработки (опять-таки деньги и сроки) и т.д. и т.п. Добавить лишний гигабайт памяти, нынче, дешевле чем оплатить час работы программера. И результат "сразу на руках" :)
Я вот тоже, вначале пытался "жатся в маленькие камни", а потом плюнул начал брать только "пожирнее". Разница в цене, совершенно не соотвествует времени затраченного на оптимизацию.
Ну, в моем случае аттини13 ~ 40руб, аттини45 ~175 руб. Цена готового устройства около 800 руб (если найдутся желающие конечно). Ну как бы.. можно и потрудится, тем более что "для сэбэ" ведь )
Да, есть места, где все таки нужен расчет прошедшего времени - и это не задержка. И оно не работает.
Приведу, может кто высмотрит ошибку..
Срабатывает "сразу". Вывести значение millis особо некуда, попробую сейчас дрыгать другими ногами для проверки..
Ну, в моем случае аттини13 ~ 40руб, аттини45 ~175 руб. Цена готового устройства около 800 руб (если найдутся желающие конечно). Ну как бы.. можно и потрудится, тем более что "для сэбэ" ведь )
Ну вот я про это и говорю. Разница 135руб. То есть <5$. Если взять "стоимость часа спеца в $50", то это разница "меньше минуты его работы". Если он провазявкается с портированием хотя-бы неделю, то страшно представить сколько конечных устройств нужно продать что-бы окупить этот переход.
Но даже если вы "не спец" и ваше время не стоит $50. Но, как-бы то нибыло, не думаю что ваше время стоит меньше $5 час.
Так что, выходит, чисто экономически это абсолютно не целесообразно. Только как "учеба" или "just for fun". "Выгодно" это может стать только при крупносерийном произвостве. Когда каждый цент умножается на десятки и сотни тысяч (и то, "сроки разработки" могут заставить выбрать более дорогой вариант).
Не вызывается ISR (TIMER0_COMPA_vect) . Полез снова в даташит )
Не заметил в приведенных вами кодах вызова функции timer_on().
И строку
void ISR (TIMER0_OVF_vect) {
стоит все же переписать в виде
ISR (TIMER0_OVF_vect) {
после удаления return куомпилятор вроде не должен ругаться. Или ворчит-таки?
И еще:
везде, где у вас используется присваивание "|=", посмотрите, а стоит ли сохранять уже установленные биты? Вот в TCCR0A, например, точно стоит все неустанавливаемые биты обнулить:
TCCR0A = ((1 << COM0A1)|(1 << COM0B1)|(1 << WGM01)|(1 << WGM00));
или по крайней мере посмотреть в даташите, а не инициализируются ли они (неустанавливаемые биты) единичками? Ведь вы же хотите в этой инструкции выбрать для каналов именно режим вывода 2, а никак не 3?
Вообще, |= и &= имеет смысл использовать, если у вас соответствующий байт/порт/регистр и в других местах изменяется и вы хотите целенаправленно "подправить" один-два бита в байте/порте/регистре. Или "набираете" нужную конфигурацию в нескольких операторах. Или используете библиотеку, которая что-то там тоже устанавливает.
Не заметил в приведенных вами кодах вызова функции timer_on().
И строку
void ISR (TIMER0_OVF_vect) {
стоит все же переписать в виде
ISR (TIMER0_OVF_vect) {
после удаления return куомпилятор вроде не должен ругаться. Или ворчит-таки?
И еще:
везде, где у вас используется присваивание "|=", посмотрите, а стоит ли сохранять уже установленные биты? Вот в TCCR0A, например, точно стоит все неустанавливаемые биты обнулить:
TCCR0A = ((1 << COM0A1)|(1 << COM0B1)|(1 << WGM01)|(1 << WGM00));
или по крайней мере посмотреть в даташите, а не инициализируются ли они (неустанавливаемые биты) единичками? Ведь вы же хотите в этой инструкции выбрать для каналов именно режим вывода 2, а никак не 3?
Вообще, |= и &= имеет смысл использовать, если у вас соответствующий байт/порт/регистр и в других местах изменяется и вы хотите целенаправленно "подправить" один-два бита в байте/порте/регистре. Или "набираете" нужную конфигурацию в нескольких операторах. Или используете библиотеку, которая что-то там тоже устанавливает.
timer_on есть, я подразумевал что он будет)
void уже убрал! Завелось все, все работает. Уже не помню, кажется на sie ругался и я его закоментил, а надо было asm("sei"); использовать
Про установку битов понял, поправлю сейчас
ну что же, прошу к оценке и здоровой критике)
Напомню цель: управление PWM мощными лампами (ближний либо дальний свет авто), сигнал Разрешено (зажигание), Запрет (ближний/дальний/аварийный даитчик масла/ручник, все через диоды), Моргать (по раздельности фарами, ака жеймсбонд)
Program: 732 bytes
--------
UDP текст программы - выключение ШИМа на пинах когда он не нужен. Лампочка не поет, нет ощущения что сидишь в трансформаторной будке)
К сожалению, на реальном устройстве на частоте 4кгц уже нагреваются мосфеты, на 30 они неуправляемы, несмотря на раскачу двумя плечами. Без драйвера видимо не обойтись если хочешь около 30 кгц.
Есть желание попробовать тот самый режим с "коррекцией фазы" и 15кгц, но наверно чуть позже пристану :)
Есть желание попробовать тот самый режим с "коррекцией фазы" и 15кгц, но наверно чуть позже пристану :)
При тех же остальных настройках вместо режима "3" выбрать "1". Частота автоматически уменьшается в два раза (за счет счета ;) счетчика :)) в обе стороны)
И фсиоо...
Понял, вечерком запробую, так, для теста. Добавил отключение пинов ШИМа когда не нужно, теперь не жужжит при выключенном состоянии)