Счетчик ATMEGA32

Обсуждаем контроллеры компании Atmel.
Ответить
Родился
Сообщения: 6
Зарегистрирован: Вт ноя 12, 2013 12:35:30

Сообщение feekus »

Здравствуйте, помогите разобраться с вот такой программой:
#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
Реклама
Друг Кота
Аватара пользователя
Сообщения: 7016
Зарегистрирован: Вс июл 12, 2009 19:15:29
Откуда: Ижевск

Сообщение pyzhman »

50кГц потому, что

Код: Выделить всё

TCNT1H=0xFF;
TCNT1L=0xFF-0x14+3;
Изменить частоту можно, забивая в регистр счёта таймера иное значение. Для 62,5кГц (грубо) это будет

Код: Выделить всё

TCNT1H=0xFF;
TCNT1L=0xF1;
Docendo discimus
Контактная информация:
Реклама
Родился
Сообщения: 6
Зарегистрирован: Вт ноя 12, 2013 12:35:30

Сообщение feekus »

А можно по конкретней про регистр 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 Гц. Исходя из чего выбирается коэффициент деления предделителя
Вымогатель припоя
Аватара пользователя
Сообщения: 672
Зарегистрирован: Ср фев 29, 2012 01:58:32
Откуда: Харьков, Украина

Сообщение eess9 »

4081h в десятичной будет 16513.

Я пользуюсь такой формулой: частота = F_CPU/(предделитель* TCNT1)
или
предделитель = F_CPU/(частота* TCNT1)

Предделитель выбираете из соображений оптимальности. Для ваших 16МГц и 62.5Гц на выходе имеем:
предделитель = 8, TCNT1 = 32000
предделитель = 64, TCNT1 = 4000
предделитель = 256, TCNT1 = 1000
предделитель = 1024, TCNT1 = 250

Дальше выбираете то, что на вас смотрит. Я бы взял первый вариант.
Реклама
Эиком - электронные компоненты и радиодетали
Друг Кота
Аватара пользователя
Сообщения: 7016
Зарегистрирован: Вс июл 12, 2009 19:15:29
Откуда: Ижевск

Сообщение pyzhman »

feekus писал(а):...не 50кГц, а 62.5
Однако, нету размерности. Посему берём предыдущую. Что вы там хотели - яснее излагайте.
Docendo discimus
Контактная информация:
Реклама
Мучитель микросхем
Сообщения: 410
Зарегистрирован: Чт ноя 13, 2008 16:33:42

Сообщение vdavid »

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 Гц, а частота - в два раза меньше. Для остальных предделителей все еще намного грустнее.
Реклама
Сверлит текстолит когтями
Аватара пользователя
Сообщения: 1262
Зарегистрирован: Пн дек 08, 2008 10:58:48
Откуда: Винница

Сообщение urry »

vdavid, прежде чем давать свои выкладки в форуме, проверяйте их в симуляторе.

1.

Код: Выделить всё

 В прерывании же записывается число на 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)

#define F_TMR1_CALC (65536ul - (FIN_TMR1/F_OUT)) //

Проект присоединяю
Вложения
kot_m16.zip
(61.75 КБ) 157 скачиваний
Контактная информация:
Мучитель микросхем
Сообщения: 410
Зарегистрирован: Чт ноя 13, 2008 16:33:42

Сообщение vdavid »

urry, Ну и чем отличаются мои -16000 от Ваших? Для чего тут нужен симулятор?
Что касается 2-х тактов, то вопрос не ко мне, а к автору кода в стартовом топике. Я лишь пытался этот код объяснить.
Друг Кота
Аватара пользователя
Сообщения: 7016
Зарегистрирован: Вс июл 12, 2009 19:15:29
Откуда: Ижевск

Сообщение pyzhman »

Не, я понимаю 2 такта для десятков кГц. Но ловить их на Гц? Хотя, конечно, всё зависит от задачи.
Docendo discimus
Контактная информация:
Мучитель микросхем
Сообщения: 410
Зарегистрирован: Чт ноя 13, 2008 16:33:42

Сообщение vdavid »

urry
А теперь посмотрите на листинг того, что Вы написали:

Код: Выделить всё

	TCNT1+=F_TMR1_CALC;
  8e:	6c b5       	in	r22, 0x2c	; 44
  90:	7d b5       	in	r23, 0x2d	; 45
  92:	80 e0       	ldi	r24, 0x00	; 0
  94:	90 e0       	ldi	r25, 0x00	; 0
  96:	0e 94 26 01 	call	0x24c	; 0x24c <__floatunsisf>
  9a:	20 e0       	ldi	r18, 0x00	; 0
  9c:	30 e8       	ldi	r19, 0x80	; 128
  9e:	41 e4       	ldi	r20, 0x41	; 65
  a0:	57 e4       	ldi	r21, 0x47	; 71
  a2:	0e 94 96 00 	call	0x12c	; 0x12c <__addsf3>
  a6:	0e 94 fa 00 	call	0x1f4	; 0x1f4 <__fixunssfsi>
  aa:	7d bd       	out	0x2d, r23	; 45
  ac:	6c bd       	out	0x2c, r22	; 44
Посчитайте, сколько тактов будет выполняться Ваше сложение и куда фактически ускачет к тому времени таймер. Погрешность здесь будет определенно больше, чем в старттопике.
Вот так будет получше, но погрешность примерно в 5 тактов процессора тоже гарантирована.

Код: Выделить всё

	TCNT1-=16000;
  7a:	8c b5       	in	r24, 0x2c	; 44
  7c:	9d b5       	in	r25, 0x2d	; 45
  7e:	80 58       	subi	r24, 0x80	; 128
  80:	9e 43       	sbci	r25, 0x3E	; 62
  82:	9d bd       	out	0x2d, r25	; 45
  84:	8c bd       	out	0x2c, r24	; 44
А всего то и надо вместо

Код: Выделить всё

#define F_TMR1_CALC (65536ul - (FIN_TMR1/F_OUT))
написать

Код: Выделить всё

#define F_TMR1_CALC (uint16_t)(65536ul - (FIN_TMR1/F_OUT))
И размер программы чудным образом уменьшится с 876 до 242 байт.
Сверлит текстолит когтями
Аватара пользователя
Сообщения: 1262
Зарегистрирован: Пн дек 08, 2008 10:58:48
Откуда: Винница

Сообщение urry »

Код: Выделить всё

Погрешность здесь будет определенно больше, чем в старттопике.
увеличьте частоту хотя бы до килогерца и сами посмотрите разницу между = и += в симуляторе.
На маленькой частоте она незаметна.

Код: Выделить всё

#define F_TMR1_CALC (uint16_t)(65536ul - (FIN_TMR1/F_OUT))
эта запись не имеет смысла, число 65536 не влезает в размер uint16_t
Контактная информация:
Мучитель микросхем
Сообщения: 410
Зарегистрирован: Чт ноя 13, 2008 16:33:42

Сообщение vdavid »

urry писал(а): эта запись не имеет смысла, число 65536 не влезает в размер uint16_t
Так зато результат влезет (а мы же преобразовываем именно его). Ведь TCNT1 - два восьмибитных регистра. И никакой лонг в него не впихнуть ну никак. Да и число 65536 нужно в этой формуле как корове седло.
Какая религия не позволяет написать так?

Код: Выделить всё

#define F_TMR1_CALC - (uint16_t)(FIN_TMR1/F_OUT)
Сверлит текстолит когтями
Аватара пользователя
Сообщения: 1262
Зарегистрирован: Пн дек 08, 2008 10:58:48
Откуда: Винница

Сообщение urry »

Так можно. И даже можно убрать приведение типов
#define F_TMR1_CALC - (FIN_TMR1/F_OUT)
Все равно при записи в 16 битный регистр оно приводится.
Контактная информация:
Мучитель микросхем
Сообщения: 410
Зарегистрирован: Чт ноя 13, 2008 16:33:42

Сообщение vdavid »

urry
Если убрать приведение типов, то все станет совсем грустно.
Предупреждение компилятора
main.c:56: warning: overflow in implicit constant conversion
и, как результат, в F_TMR1_CALC нолик.
Ну и сгенеренный код:

Код: Выделить всё

	TCNT1+=F_TMR1_CALC;
  8e:	6c b5       	in	r22, 0x2c	; 44
  90:	7d b5       	in	r23, 0x2d	; 45
  92:	80 e0       	ldi	r24, 0x00	; 0 !!!!!!!!!!
  94:	90 e0       	ldi	r25, 0x00	; 0 !!!!!!!!!!
  96:	0e 94 24 01 	call	0x248	; 0x248 <__floatunsisf>
  9a:	20 e0       	ldi	r18, 0x00	; 0
  9c:	30 e0       	ldi	r19, 0x00	; 0
  9e:	4a e7       	ldi	r20, 0x7A	; 122
  a0:	56 e4       	ldi	r21, 0x46	; 70
  a2:	0e 94 93 00 	call	0x126	; 0x126 <__subsf3>
  a6:	0e 94 f8 00 	call	0x1f0	; 0x1f0 <__fixunssfsi>
  aa:	7d bd       	out	0x2d, r23	; 45
  ac:	6c bd       	out	0x2c, r22	; 44
В Вашим первоначальном примере приведение типов отложено на run-time, что приведет более чем к трехкратному увеличению кода. Это раз. Во-вторых конечный результат что в первой предложенной мной конструкции, что во второй - один и тот же - число 0xc180 (или -16000). Ну и в-третьих. Зачем мне смотреть что-то в симуляторе, когда все очевидно. При входе в прерывание мы поместили значение TCNT1 в регистровую пару r22,r23. Легко сосчитать по тактам, что число это будет равно 2. Далее мы вызываем подпрограммы __floatunsisf, __subsf3 и __fixunssfsi. Трассировать мне их лень, но очевидно что выполнение их займет десятки, а скорее сотни тактов CPU. За это время TCNT1 тикнет не один раз. Но мы то прибавляем к первоначальной двоечке. Так понятно?
Последний раз редактировалось vdavid Вт сен 30, 2014 14:55:39, всего редактировалось 1 раз.
Сверлит текстолит когтями
Аватара пользователя
Сообщения: 1262
Зарегистрирован: Пн дек 08, 2008 10:58:48
Откуда: Винница

Сообщение urry »

Раздутость кода объясняется приведением числа флоат - #define F_CALC 62.5
Поставьте здесь например #define F_OUT (uint16_t)(F_CALC*2)
и оно исчезнет
Если вы не хотите делать симуляцию, чтобы увидеть разницу между + и += , за это вас никто делать не будет.
Удачи типа
Контактная информация:
Опытный кот
Аватара пользователя
Сообщения: 882
Зарегистрирован: Ср фев 22, 2012 01:25:21

Сообщение shads »

urry писал(а):за это вас никто делать не будет.
Можно уточнить, какой именно смысл вы вложили в эту фразу :)...
Мучитель микросхем
Сообщения: 410
Зарегистрирован: Чт ноя 13, 2008 16:33:42

Сообщение vdavid »

urry Хорошо. Ответьте мне по-простому. Зачем в этой задаче плавающая точка в ран-тайме? Что касается разницы, то да, она будет но не в пользу Вашего кода. Почему? По-моему я доходчиво объяснил. Насчет симуляции... Совершенно не обязательно пробовать на зуб гранит для того, что бы убедиться что он таки твердый.
Опытный кот
Аватара пользователя
Сообщения: 882
Зарегистрирован: Ср фев 22, 2012 01:25:21

Сообщение shads »

urry писал(а):

Код: Выделить всё

#define F_TMR1_CALC (uint16_t)(65536ul - (FIN_TMR1/F_OUT))
эта запись не имеет смысла, число 65536 не влезает в размер uint16_t
urry - "поспешишь - людей насмешишь..." ну ничьо, с кем не бывает :)...
vdavid писал(а):urry Хорошо. Ответьте мне по-простому. Зачем в этой задаче плавающая точка в ран-тайме?
urry занят... отвечу за него :) - конкретно в данной задаче - НЕ ЗАЧЕМ... и даже если бы она была не в ран тайме, все равно странно.. что она используется...
Ответить

Вернуться в «AVR»