ВходНаше всё Теги codebook 无线电组件 Поиск Опросы Закон Среда
26 марта
1505287
Adept (12.03.2025 00:53 - 01:01, просмотров: 945)
может кому пригодится, - синхронизация CMP/OVF таймеров RTC. Пока тут искал и исправлял собственные баги, наткнулся на ожидаемое, и неприятное поведение RTC, впрочем, вероятно свойственно и другим таймерам, когда хочется использовать ресурсы таймера по максимуму, в частности 

вот, на асинхронном таймере RTC, я сделал два аппаратныйх системных таймера (три точнее)

1) по "compare" (регистр COMPARE) - 1mS таймер

2) по "overflow" (точнее регистр PERIOD) - таймер 10mS

3) PIT (интервальный таймер, по сути - делитель частоты кварца) - собственно RTC (время)

дык о проблеме - в m4809 (AVR2), RTC при переполнении (равенстве регистру PERIOD) сбрасывает счётные регистры таймера в ноль, в связи с чем возникают "краевые эффекты" - на конце диапазона конкурентное срабатывание таймеров Compare и Overflow, что выражается в том, что прерывания compare может не случиться. Особенно это отягощено тем, если задействованы ещё какие прерывания.

если таймеры организованы так: - по прерыванию CMP, считываем CMP-регистр и прибавляем к нему константу для отсчёта следующего интервала 1mS

, а по прерыванию OVF (начинается отсчёт нового 10mS периода, при этом счётные регистры аппаратно обнуляются) инициализирцуем CMP-регистр значением константы для 1mS, то

в итоге, может возникнуть ситуация, когда на краю диапазона сначала отработал таймер OVF (обнулив регистры CMP, когда период отсчёта 1mS почти закончился), а потом начинается отсчёт ещё одного интервала 1mS, в итоге в метках времени возникает пауза в 1mS.


я победил это анализом содержимого "compare" регистра на момент прерывания OVF и принудительного запуска хэндлера 1mS

вот так примерно:

кто-то предложит изящнее (т.к. чувствую какую-то корявость.... но объяснить не могу где... как-то неестественно всё... так и "выпирают костыли" :)))


Int_RTC_timers:		//
	PUSH_TMP_SREG		;\
	push	TMP2		;\\
	PUSH_X			;\\\
	//т.к. прерывание общее для двух событий (CMP и OVF), то обрабатываем оба :)
	rcall	RTC1mS_subhandler	; обработчик события №1 - сравнение (таймер 1mS)
	rcall	RTC10mS_subhandler	; обработчик события №2 - переполнение (таймер 10mS)
	;				;  (в нём так же происходит и пересброс регистров Compare)
	lds	TMP,RTC_INTFLAGS
	sts	RTC_INTFLAGS,TMP	;Сбросим все активные ф.прерываний (т.к. для RTC это надо делать "врукопашную"
					;  ввиду комбинированного события CMP/OVF на одном и том же прерывании.
	;------------
Int_RTC_Timers_RET:
	POP_X			;///
	pop	TMP2		;//
	POP_TMP_SREG		;/
	ret
;-----------------------------------------------------------------
RTC1mS_subhandler:
//т.к. это "дочерние" П/П хэндлера, то уже сохранены регистры TMP,TMP2,X
	lds	TMP,RTC_INTFLAGS
	sbrs	TMP,RTC_CMP_bp		//т.к. прерывание общее для двух событий (CMP и OVF), то выделяем "своё" :)
	rjmp	RTC1mS_subhandler_RET	;если не было "своего" прерывания, то сразу уходим,
					;  иначе обрабатываем его.
	;------------------------------
	// действия в хэндлере 1mS
RTC1mS_action:
	; !!! если есть операции со стеком, то PUSH-и надо располагать здесь, т.к. работаем так же и
	;     с выовом по метке "RTC1mS_action" !!!
	;push ...	;\

	call	Update_RTC_1mS		;Обновляем значение Compare-регистра RTC
	S_INC	S_1mS_RTC		;RTC счётчик 1mS (обнуляется каждые 10mS)

#warning "DBG 11-03-25 RTC-marker 1mS"
MARKER_FLASH_PORTD	1

	;------------------------------
RTC1mS_subhandler_RET:
	;pop ...	;/
	ret
;-----------------------------------------------------------------
RTC10mS_subhandler:
	PUSH_Y			;Сохраним дополнительно используемые регистры
//т.к. это "дочерние" П/П хэндлера, то уже сохранены регистры TMP,TMP2,X
	lds	TMP,RTC_INTFLAGS
	sbrs	TMP,RTC_OVF_bp		//т.к. прерывание общее для двух событий (CMP и OVF), то выделяем "своё" :)
	rjmp	RTC10mS_subhandler_RET	;если не было "своего" прерывания, то сразу уходим,
					;  иначе обрабатываем его.
	;------------------------------
	// действия в хэндлере 10mS
/*
т.к. "CMP" и "OVF" на границе периода будут наступать практически одновременно (но есть неопределённость при использовании
  других прерываний), то возможны две ситуации:
ситуация 1:
	событие "сравнение" было "вот-только что"
	и значение compare-регистра далеко "за" текущим значением счётчика
	а по "overflow" (PERIOD) счётные ригистры RTC сбрасываются, и "compare" регистры инициализируются значением периода.
	В этом случае всё будет штатно, "compare" прерывание сработает вовремя практически без ошибки.
ситуация 2:
	событие "сравнение" было относительо давно и новое должно быть "вот-вот")
	новое значение compare-регистра ДОЛЖНО БЫТЬ назначено вскоре после только что случившегося переполнения
	а по "overflow" (PERIOD) счётные ригистры RTC сбрасываются, и "compare" регистры инициализируются значением периода,
	В этом случае "compare" прерывание сработает фактически с двойным интервалом

для избежания этой ситуации нужно анализировать текущее значение "Compare"  регистра и если оно больше, чем значения
регистра переполнения (PERIOD), то необходимо принудительно выполнить хэндлер 1mS
*/
	rcall	wait_RTC_CMPbusy	;Синхронизируем доступ к регистру Compare
	;
	lds	XL,RTC_CMPL		;считаем текущее значение регистров сравнения
	lds	XH,RTC_CMPH
	;
	ldi	TMP,low(RTC_10mS_constant)	;То, что должно быть в таймере на момент возникновения OVF-прерывания (переполнение)
	ldi	TMP2,high(RTC_10mS_constant)
	cp	XL,TMP				;Сравним "CMP"-регистр и и текущее значение таймера (равного в данный момент
	cpc	XH,TMP2				;  константе периода переполнения)
	brcs	RTC10mS_normal			;Если CMP меньше текущего значения (значит работаем штатно )
	call	RTC1mS_action			;  иначе, сначала выполним действия хэндлера 1mS.
RTC10mS_normal:
	call	Init_RTC_1mS		;инициализируем 1mS-compare-регистры (т.к. таймер сбрасывается по PERIOD-Overflow)
	;-----
	// Действия по таймеру 10mS

#warning "DBG 11-03-25 RTC-marker 10mS"
MARKER_FLASH_PORTD	2

	S_INC	S_10mS_RTC		;RTC счётчик 10mS (обнуляется каждые 100mS)
	S_CLR	S_1mS_RTC		;обнуление RTC счётчика 1mS (обнуляется каждые 10mS)
	;

	;------------------------------
RTC10mS_subhandler_RET:
	POP_Y			;Восстановим дополнительно используемые регистры
	ret

...делать нужно так, как нужно. А как ненужно - делать не нужно (С) Винни-Пух :)