Программные задержки для STM8

Доброго здравия, All.

Ковыряю STM8 контроллеры, и часто сталкиваюсь с тем, что нет каких-то мелочей, к которым привык. На этот раз такой мелочью стали программные задержки. Например, в AVR-LibC была для этого delay.h.

Её-то я «интерфейс» я и повторил под осваиваемого зверька. Можно спорить об актуальности программных задержек, но бывает, что всё же нет альтернативы. Например — это часто проще, чем возиться с таймером. Или когда требуется формировать короткие промежутки времени: вход и выход из прерывания требуют довольно большого количества тактов, а значит — просто не успеем. Есть ещё фактор: когда время входа в прерывание плохо предсказуемо, и требует особого внимания.

Собственно, файл брать здесь.
27.11.2014 Не упомянул компилятор, для какого тестировал — COSMIC. Зато теперь работает и для SDCC.
  • +1
  • 23 октября 2014, 11:59
  • Hoksmur

Комментарии (31)

RSS свернуть / развернуть
Мммм, серьезно? простоя задержка стала поводом для топика?
-4
И да, и нет. Повод — незначительный, в этом вы правы. Просто надоело высчитывать количество циклов для разных частот, да ещё для разных задержек. А тут — переложил это на компилятор — у него мозги кремниевые, не болят… А готового подобного решения не видел.
0
Good news, everyone!
+1
Bsd news, everyone. В этой хреновине есть конвейер, а значит, задержки будут непредсказуемы…
0
Да, трёхступенчатый. Много есть в доке PM0044 Programming manualНе претендую на точность выдержек, но мне хватило. По идее, критичные импульсы таймером формировать без участия «мозга», благо регистров там хватает.
По конвейеру — время выполнения команд предсказуемо, но вот загрузку его я так и не осилил полностью по вышепреведённому документу. Точнее — сколько будет ждать после выполнения перехода. Ожидал ошибки задержек от расчётных величин в сторону увеличения, однако на практике оказалось наоборот. Или что-то неправильно считал.
+1
Очень тяжело предсказать время выполнения, у одного и того же кода в зависимости от расположения в памяти время выполнения может сильно отличаться. Когда делал программный USB на STM8 пришлось повозится с этой херней :(
0
Читал результат в блоге — запускать таймер от того же вывода, от которого ловим изменение ножки — красивое решение!
0
Спасибо.
Другого решения просто не смог найти, да и наверное его нет ;)
0
Как я понимаю, гадит не столь конвеер, сколь особенности доступа к флешу? Что-то вроде кеша или выравнивания команд?
0
Если есть желание посмотреть самому — Подраздел 5.3, страница 23 выше по ссылке и дальше. Например, в 5.4.1:
In the example shown in Table 6, the code is stored in the Flash Program memory (32-bit bus). As a result, 3 cycles are needed to fill the 96-bit prefetch buffer. At each cycle, one word is loaded and stored in F1, F2 and F3. The next fetch operation can start only when all the instructions contained in one of the Fx word are decoded. In fact, at cycle 9, the last instruction contained in F3(SWAP A) is decoded, and a fetch operation can start to fill F3 word.
0
Ну почему же, на мой взгляд он тоже не позволяет определить точное время выполнения команд.
0
Ну, не до такой степени, чтобы зависеть от расположения команд в памяти. По моему, более предсказуемая штучка.
0
На худой конец, можно отсчитать задержку по таймеру, но поллингом, без прерываний.
Опять же, организация в виде отдельного модуля позволяет абстрагироваться от конкретной реализации
0
Программные задержки- зло, хотя в некоторых простеньких проектах- их достаточно.
-2
Каждый раз, когда ты пишешь в обработчике прерывания задержку, в мире умирает котенок(C)
+2
так я ж говорю зло
0
Таки кто вас заставляет делать это? Задержки в прерываниях? «Месьё знает толк в извращениях!» (с)
+1
У AVR асмовская вставка объявлена как volatile, а у Вас нет. Это конечно зависит от компилятора, но теоретически оптимизатор может выбросить вашу задержку из кода.
+1
  • avatar
  • e_mc2
  • 24 октября 2014, 11:34
работа полезная, но, увы, не работает.

$ make
sdcc -mstm8 -lstm8 --out-fmt-ihx   -o blinky.ihx blinky.c
In file included from blinky.c:9:
delay.h:20: syntax error: token -> 'void' ; column 18
make: *** [blinky.ihx] Error 1


особый синтаксис?

собираю этой версией (свежак):
$ sdcc -v
SDCC : mcs51/z80/z180/r2k/r3ka/gbz80/tlcs90/ds390/pic16/pic14/TININative/ds400/hc08/s08/stm8 3.4.0 #8981 (Oct 25 2014) (Linux)
0
  • avatar
  • Doka
  • 26 ноября 2014, 19:34
Это под Cosmic писал. Похоже, что SDCC не переварил комбинацию static inline void. Посмотрите в документации, как в используемом вами компиляторе оформить следует.
0
Попробуйте оставить только inline void
0
да-да… нарыл что нужно включить STD99, включил, также переписал ассемблерную вставку согласно синтаксиса, убрал const из объявления переменной передаваемой функции, теперь интереснее:

sdcc -mstm8 -lstm8 --std-sdcc99 -DF_CPU=8000000 --out-fmt-ihx   -o blinky.ihx blinky.c
delay.h:24: warning 85: in function _delay_cycl unreferenced function argument : '__ticks'
blinky.asm:116: Error: <m> multiple definitions error
blinky.asm:116: Error: <p> phase error: label location changing between passes 2 and 3


подскажите как эти де строчки переделать в одну:
//ldw X, __ticks ; insert automaticaly
        __asm("nop\n $N:\n decw X\n jrne $L\n nop\n ", __ticks);

поскольку у инлайнового ассмблера такой синтаксис:
__asm__ (”; This is a comment\nlabel:\n\tnop”);

т.е. никаких запятых внутри скобок он не понимает.
0
У Cosmic компилятор перед асм-фрагментом кладёт __ticks в регистр X.
Получается:
nop
label_1:
decw X
jrne label_1
nop
nop в начале и в конце добавил для выравнивания тактов. Можете оставить только один-два nop внутри и скинуть получившийся листинг, зацепив несколько строк перед ней?
0
У SDCC в документации почти готовый рецепт в пункте 3.14.4 Use of Labels within Inline Assembler
Поделитесь результатом? Я потом в файлик добавлю.
0
;--------------------------------------------------------
; code
;--------------------------------------------------------
	.area CODE
;	delay.h: 20: static inline void _delay_cycl( unsigned short __ticks )
;	-----------------------------------------
;	 function _delay_cycl
;	-----------------------------------------
__delay_cycl:
;	delay.h: 24: __asm__("nop\n 00001$:\n decw X\n jrne 00001$\n nop\n ");
	nop
	 00001$:
	decw X
	jrne 00001$
	nop
	ret
;	delay.h: 27: static inline void _delay_us( unsigned short __us )
;	-----------------------------------------
;	 function _delay_us
;	-----------------------------------------


а вот с добавлением «ldw X, __ticks» проверьте:

;	delay.h: 22: static inline void _delay_cycl( unsigned short ticks )
;	-----------------------------------------
;	 function _delay_cycl
;	-----------------------------------------
__delay_cycl:
;	delay.h: 25: __asm__("ldw X,_ticks\n nop\n 00001$:\n decw X\n jrne 00001$\n nop\n ");
	ldw X,_ticks
	nop
	 00001$:
	decw X
	jrne 00001$
	nop
	ret
;	delay.h: 28: static inline void _delay_us( unsigned short __us )


сам синтаксис:
unsigned short ticks;

static inline void _delay_cycl( unsigned short ticks )
{
        ticks; // sdccman.pdf:  to avoid warning  "unreferenced function argument"
        __asm__("ldw X,_ticks\n nop\n 00001$:\n decw X\n jrne 00001$\n nop\n ");
}
0
есть еще одна проблема, связанная с метками в ассемблере: если вызывать процедуру delay из нескольких мест — в итоговом файле используетс одна и та же метка, и в результате вываливается:
blinky.asm:188: Error: <p> phase error: label location changing between passes 2 and 3
blinky.asm:188: Error: <m> multiple definitions error
blinky.asm:217: Error: <p> phase error: label location changing between passes 2 and 3
blinky.asm:217: Error: <m> multiple definitions error 
0
do {
		ticks--;
	} while (ticks);
собирается компилятором в
ldw	x, #0x007f
00104$:
	decw	x
	tnzw	x
	jrne	00104$

Проще пересчитать такты, при необходимости добавить nop для выравнивания циклов, и поправить формулу в _delay_us
Но я только завтра до этого доберусь.
0
Попробуйте заменить тело функции _delay_cycl на следующий фрагмент:
#if defined(__CSMC__)
/* COSMIC */
  #define T_COUNT(x) (( F_CPU * x / 1000000UL )-3)/3)
	// ldw X, __ticks ; insert automaticaly
	_asm("nop\n $N:\n decw X\n jrne $L\n nop\n ", __ticks);
#elif defined(__SDCC)
  #define T_COUNT(x) (( F_CPU * x / 1000000UL )-5)/5)
	__asm("nop\n nop\n");
	do { 		// ASM: ldw X, #tick; lab$: decw X; tnzw X; jrne lab$
                ticks--;//      2c;                 1c;     2c    ; 1/2c   
        } while (ticks);
	__asm("nop"); // allign last cycle
#elif defined(__RCST7__)
/* RAISONANCE */
  #error "ToDo for RAISONANCE"
#elif defined(__ICCSTM8__)
/* IAR */
  #error "ToDo for IAR"
#else
 #error "Unknown Compiler!"          /* Compiler defines not found */
#endif

PS: Похоже, IAR не умеет inline. Сколько не пытался — всегда call использует.
0
И в _delay_us на
_delay_cycl( (unsigned short)( T_COUNT(__us) );
0
спасибо, вроде завелось, вот asm:

;--------------------------------------------------------
; code
;--------------------------------------------------------
	.area CODE
;	delay.h: 22: static inline void _delay_cycl( unsigned short ticks )
;	-----------------------------------------
;	 function _delay_cycl
;	-----------------------------------------
__delay_cycl:
;	delay.h: 33: __asm__("nop\n nop\n");
	nop
	nop
;	delay.h: 34: do {            // ASM: ldw X, #tick; lab$: decw X; tnzw X; jrne lab$
	ldw	x, (0x03, sp)
00101$:
;	delay.h: 35: ticks--;//      2c;                 1c;     2c    ; 1/2c   
	decw	x
;	delay.h: 36: } while (ticks);
	tnzw	x
	jrne	00101$
;	delay.h: 37: __asm__("nop"); // allign last cycle
	nop
	ret
;	delay.h: 49: static inline void _delay_us( unsigned short __us )
;	-----------------------------------------


PS: по синтаксису sdcc асм вставки вместо __asm(«nop»); надо оформлять как __asm__(«nop»);
0
Проблема в другом. Я так и не нашёл, как в SDCC передать параметр в асм-вставку.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.