Здравствуйте, помогите разобраться с вот такой программой:
#define GEN 0
#define GEN_PORT PORTB
#define GEN_DDR DDRB
#include <avr/io.h>
#include <avr/interrupt.h>
ISR(TIMER1_OVF_vect)
{
TCNT1H=0xFF;
TCNT1L=0xFF-0x14+3;
GEN_PORT^=1<<GEN;
sei();
}
int main(void)
{
GEN_DDR|=1<<GEN;
TCCR1B|=1<<CS11;
TCNT1H=0xFF;
TCNT1L=0xFF-0x14+1;
TCCR1B|=(1<<WGM12|1<<WGM13);
TIMSK|=1<<TOIE1;
sei();
while(1){}
}
В результате выполнения этой программы микроконтроллером на
выводе PB0, генерируется прямоугольный сигнал с частотой 50 кГц и
длительностью импульса 10 мкс., почему 50кГц генерируется и как выудить из программы например не 50кГц, а 62.5
А можно по конкретней про регистр TCNT1 и метод записи
TCNT1H=0xFF- это старший байт
TCNT1L=0xFF-0x14+3 это младший байт
какое число получится если перевести его в 10 систему 4081 ?
Нашел вот такое объяснение относительно времени прерывания: Устанавливаем значение счетного регистра TCNT1, таким образов чтобы прерывание происходило каждые 10 мс. Расчет производится по
следующей формуле
TCNT1=0xFFFF-(T*F_CPU/K)+1,
где T–желаемая длительность, F_CPU–тактовая частота контроллера
иK – коэффициент деления предделителя. Таким образом для нашей
задачи получим:
TCNT1=0xFFFF-(10*10^-6*16*10^6/8)+1=0xFFFF-0x14+1.
а как именно из него частота получается, при том что у данной программы стоит прерывание каждые 10мс(может из них получить 50кГц)
P.S я хотел получить не 62.5кГц, а 62.5 Гц. Исходя из чего выбирается коэффициент деления предделителя
feekus Не забывайте, что счетчик считает вперед. Т.е., когда Вы записали в него число 0xffff+1-20(0x14), то до нуля вверх он отсчитает ровно 20 тактов. С учетом предделителя на 8 частота прерываний будет 16 000 000/20/8=100 0000 Гц, т.е. период будет именно 10 мс. В прерывании же записывается число на 2 меньше в предположении, что на вход в прерывание потратилось 2 такта счетчика. Лично я бы так не делал. Ведь можно задать TOP для счетчика и не заморачиваться с подсчетом тактов.
Если же Вы хотите сохранить этот код, то для получения 62.5 Гц должно быть так:
TCNT1=0xffff-16000+1
или более по-человечески
TCNT1=-16000
Ну и до кучи sei() в прерывании как бы не нужен вовсе.
eess9 Если брать код из стартового топика то Ваши советы есть неправильные. Для предделителя 8, например, полупериод будет
16 000 000/(65536-32000)/8=59.637 Гц, а частота - в два раза меньше. Для остальных предделителей все еще намного грустнее.
В прерывании же записывается число на 2 меньше в предположении, что на вход в прерывание потратилось 2 такта счетчика.
Вы никогда не можете сказать, сколько времени займет вход в прерывание - почему именно 2 такта счетчика ? С этим борятся , прибавляя в прерывании вычисленное значение - какое-то улучшение точности есть.
ISR (TIMER1_OVF_vect)
{
TCNT1+=F_TMR1_CALC;
BITINV(PORTB,0);
}
2. Формула расчета выходной частоты предельно простая
#define FIN F_CPU/8ul
#define F_CALC 62.5
#define F_OUT (F_CALC*2)
urry, Ну и чем отличаются мои -16000 от Ваших? Для чего тут нужен симулятор?
Что касается 2-х тактов, то вопрос не ко мне, а к автору кода в стартовом топике. Я лишь пытался этот код объяснить.
Посчитайте, сколько тактов будет выполняться Ваше сложение и куда фактически ускачет к тому времени таймер. Погрешность здесь будет определенно больше, чем в старттопике.
Вот так будет получше, но погрешность примерно в 5 тактов процессора тоже гарантирована.
urry писал(а):
эта запись не имеет смысла, число 65536 не влезает в размер uint16_t
Так зато результат влезет (а мы же преобразовываем именно его). Ведь TCNT1 - два восьмибитных регистра. И никакой лонг в него не впихнуть ну никак. Да и число 65536 нужно в этой формуле как корове седло.
Какая религия не позволяет написать так?
urry
Если убрать приведение типов, то все станет совсем грустно.
Предупреждение компилятора
main.c:56: warning: overflow in implicit constant conversion
и, как результат, в F_TMR1_CALC нолик.
Ну и сгенеренный код:
В Вашим первоначальном примере приведение типов отложено на run-time, что приведет более чем к трехкратному увеличению кода. Это раз. Во-вторых конечный результат что в первой предложенной мной конструкции, что во второй - один и тот же - число 0xc180 (или -16000). Ну и в-третьих. Зачем мне смотреть что-то в симуляторе, когда все очевидно. При входе в прерывание мы поместили значение TCNT1 в регистровую пару r22,r23. Легко сосчитать по тактам, что число это будет равно 2. Далее мы вызываем подпрограммы __floatunsisf, __subsf3 и __fixunssfsi. Трассировать мне их лень, но очевидно что выполнение их займет десятки, а скорее сотни тактов CPU. За это время TCNT1 тикнет не один раз. Но мы то прибавляем к первоначальной двоечке. Так понятно?
Последний раз редактировалось vdavid Вт сен 30, 2014 14:55:39, всего редактировалось 1 раз.
Раздутость кода объясняется приведением числа флоат - #define F_CALC 62.5
Поставьте здесь например #define F_OUT (uint16_t)(F_CALC*2)
и оно исчезнет
Если вы не хотите делать симуляцию, чтобы увидеть разницу между + и += , за это вас никто делать не будет.
Удачи типа
urry Хорошо. Ответьте мне по-простому. Зачем в этой задаче плавающая точка в ран-тайме? Что касается разницы, то да, она будет но не в пользу Вашего кода. Почему? По-моему я доходчиво объяснил. Насчет симуляции... Совершенно не обязательно пробовать на зуб гранит для того, что бы убедиться что он таки твердый.
эта запись не имеет смысла, число 65536 не влезает в размер uint16_t
urry - "поспешишь - людей насмешишь..." ну ничьо, с кем не бывает ...
vdavid писал(а):urry Хорошо. Ответьте мне по-простому. Зачем в этой задаче плавающая точка в ран-тайме?
urry занят... отвечу за него - конкретно в данной задаче - НЕ ЗАЧЕМ... и даже если бы она была не в ран тайме, все равно странно.. что она используется...