|
|
  |
Циклический буфер на AVR, (Примеры применения) |
|
|
|
Aug 24 2007, 15:16
|

Ambidexter
    
Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282

|
Тема открыта по горячим следам недавней острой дискуссии. В ней одним из участников (defunct) была приведена программа, в которой использовался циклический буфер для работы с последовательным портом МК AT90S2313. На взгляд автора программа написана достаточно грамотно, но как-то слишком размашисто, без учёта мизерности ресурсов данного МК. Предлагаю участникам поделиться примерами и приёмами использования циклического буфера в программных разработках. Для затравки предлагаю следующие фрагменты. А. Запись байта из регистра AL в циклический буфер Код mov xl,qhead ;указатель на запись st x+,al ;запишем байт cp xl,qend ;конец буфера? brne .+2 ;нет, обходим ldi xl,buffer ;да, установим начало Б. Чтение байта из циклического буфера в регистр AL Код mov xl,qtail ;указатель на чтение ld al,x+ ;прочитаем байт cp xl,qend ;конец буфера? brne .+2 ;нет, обходим ldi xl,buffer ;да, установим начало Если использовать буфер не произвольной длины, а кратный степени 2, и размещать его в памяти не произвольно, а начиная с адресов, кратных длине буфера, то размер кода можно немного сократить. Например. В. Запись байта из регистра AL в циклический буфер длиной 32 Код mov xl,qhead ;указатель на запись st x+,al ;запишем байт cbr xl,0xE0 ;держим указатель в предписанных рамках Г. Чтение байта из циклического буфера длиной 32 в регистр AL Код mov xl,qtail ;указатель на чтение ld al,x+ ;прочитаем байт cbr xl,0xE0 ;держим указатель в предписанных рамках Здесь qhead, qtail, qend – регистровые переменные, содержащие адреса-указатели ячеек в озу для записи, чтения и конца буфера соответственно.
--------------------
Делай сразу хорошо, плохо само получится
|
|
|
|
Guest_=AVR=_*
|
Aug 25 2007, 05:42
|
Guests

|
Такой метод (посредством урезания операцией "AND") организации кольцевых буферов с длиной и начальным адресом, кратными 2^N, весьма удобен и эффективен - недаром он широко применяется аж с 80-х годов. Эффективнее него - только аппаратные кольцевые буфера, реализованные в некоторых DSP, а также в dsPIC. =GM= молодец - очень полезно время от времени напоминать широким массам о том, что Волга впадает именно в Каспийское море, а не в Северное
Сообщение отредактировал =AVR= - Aug 25 2007, 06:01
|
|
|
|
|
Aug 25 2007, 07:48
|
Знающий
   
Группа: Свой
Сообщений: 709
Регистрация: 3-05-05
Пользователь №: 4 693

|
Цитата(=GM= @ Aug 24 2007, 19:16)  Код mov xl,qhead;указатель на запись st x+,al;запишем байт cp xl,qend;конец буфера? brne .+2;нет, обходим ldi xl,buffer;да, установим начало ... ИМХО, выделять регистровые переменные под хранение начала-конца для тонких АВР весьма тоскливо. Если прально понял, то всё это богацтво хранится вечно, бо чтение ЦБ осусчествляется в одном месте, запись - в другом... Регистр, опять-же, ИМХО, гораздо дороже ячейки в ОЗУ...Хотя... Код .dseg qhead: .byte 1 buffer: .byte N qend: other_data:
... lds xl,qhead;указатель на запись st x+,al;запишем байт cpi xl,qend;конец буфера? brne no_cycled;нет, обходим ldi xl,buffer;да, установим начало no_cycled: sts qhead,xl в пассиве три такта, два слова и лишний указатель в ОЗУ. Но зато есть лишний регистер и ничего не остаётся в регистрах надолго. Но, как правило, в ЦБ кто-то пишет/читает постепенно и одновремено. Могут столкнуться и перетолкнуться, значит надо есчо и указатели контролировать. Код .dseg qread: .byte 1 qwrite: .byte 1 buffer: .byte N qend: other_data:
... lds xl,qwrite;указатель на запис lds r0,qread;на чьтение cp xl,r0 brne pointers_ok ; ;ой, мля, чё делать-то?!! ; pointers_ok: st x+,al;запишем байт cpi xl,low(qend);конец буфера? brne no_cycled;нет, обходим ldi xl,low(buffer);да, установим начало no_cycled: sts qwrite,xl ... Вроде нигде не накосячил...А?
|
|
|
|
|
Aug 26 2007, 10:50
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(=AVR= @ Aug 25 2007, 08:42)  Такой метод (посредством урезания операцией "AND") организации кольцевых буферов с длиной и начальным адресом, кратными 2^N, весьма удобен и эффективен - недаром он широко применяется аж с 80-х годов. Эффективнее него - только аппаратные кольцевые буфера, реализованные в некоторых DSP, а также в dsPIC. =GM= молодец - очень полезно время от времени напоминать широким массам о том, что Волга впадает именно в Каспийское море, а не в Северное  1) К сожалению не всегда применимо. 2) При написании программы - реализация кольцевого буфера - единицы %. Поэтому я за написание данного участка "размашисто". Зато нет необходимости лично контролировать размещение самого буфера. Я тоже применял сначала такой метод. Кстати возможно автоматическое размещение буфера на границу по типу применяемой мной привязки таблицы данных Код .cseg .org (pc & $ff80)+$80 Приведу пример буфера несколько сложнее организованного Код ;**************************************************************** ;* Вывод символа (wl) в буфер вывода. При выводе контроли- * ;* руется заполнение буфера. При переполнении буфера вывод тор- * ;* мозится, и подпрограмма не завершится, пока он не закончится.* ;* Две точки входа: outwl и outwlsZ; * ;* Портятся tmph, Z. (для outwl) * ;* Портятся tmph. (для outwlsZ) * ;****************************************************************
outwl: mov tmph, TBH sub tmph, TBE brcc outw1 subi tmph, -lBuf outw1: cpi tmph, 2 breq outwl mov Zl, TBE ldi ZH, high(TxBuf) st Z+, wl cpi Zl, lBuf brne outw2 clr Zl outw2: mov TBE, Zl ret Приём Код ;**************************************************************** ;* Прерывание на приём данных. * ;* * ;* Принятый байт размещается в кольцевом буфере RxBuf. Размер * ;* кольцевого буфера lBuf байт. В случае когда до заполнения * ;* буфера осталось 16 байт снимается готовность модема. (Аппа- * ;* ратным или програмным способом. При освобождении буфера на * ;* половину готовность опять включается в голове. * ;* Используются регистры RBH и RBE как мл. байт адреса указа- * ;* телей на голову и хвост буфера соответственно. Портятся ре- * ;* гистр wp и регистровая пара X. * ;* Максимальное время выполнения: 31 такт. * ;****************************************************************
RxUART: in tmpsreg, sreg mov Xl, RBE; Поместить его в регистровую пару X clr Xh in wp, udr; Прочитать принятый байт и st X+, wp; поместить его в буфер cpi Xl, RxBuf+lBuf; Конец буфера? brne RxU1 ; если нет, то дальше ldi Xl, RxBuf; а иначе в начало буфера RxU1: mov RBE, Xl; и сохранить mov Xl, RBH; Определить объём свободного места sub Xl, RBE; в буфере brcc RxU2 subi Xl, -lBuf; Откорректировать при перехлёсте RxU2: cpi Xl, 16; Осталось меньше 16 байт? brsh RxUE ; если нет, то выйти
sbi portd, CTS; Сбросить готовность модема
RxUE: lds Xl, s2 cp wp, Xl brne plclr tst nplusC brne plinc tst ms20 breq plinc plclr: clr nplusC rjmp plset plinc: inc nplusC plset: lds ms20, s12; сбросить задержку out sreg, tmpsreg reti Это реализация старая 2003 года последние изменения. Сейчас вообще буфер большой. В байт не влазит. На мелочи не обращать внимание просто по живому вырезалось.
|
|
|
|
|
Aug 28 2007, 16:02
|

Ambidexter
    
Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282

|
Привет всем! Отъезжал ненадолго, тут праздник местный был (summer bank holiday), теперь будем смотреть ответы и отвечать по мере сил-возможностей(:-) 1) To SasaVitebsk. Как всегда, приведен добротный код. Есть только мелкие вопросики. Фрагмент ниже непонятен, вроде бы надо буфер разместить в озу, а у вас стоит пзу. Цитата(SasaVitebsk @ Aug 26 2007, 09:50)  Кстати возможно автоматическое размещение буфера на границу по типу применяемой мной привязки таблицы данных Код .cseg .org (pc & $ff80)+$80 Трудно разобраться с передачей, метка outwl указана дважды, а метки outwlsZ нет вовсе. Цитата(SasaVitebsk @ Aug 26 2007, 09:50)  Код ;* Две точки входа: outwl и outwlsZ; * ;* Портятся tmph, Z. (для outwl) * ;* Портятся tmph. (для outwlsZ) * ;**************************************************************** outwl: mov tmph, TBH sub tmph, TBE brcc outw1 subi tmph, -lBuf outw1: cpi tmph, 2 breq outwl ret 2) Как обычно ничем не удивил и не порадовал широкоизвестный в узких кругах эникейщик =AVR=, вроде бы и похвалил, вроде бы и пожурил в одно и то же время, но что такое "аппаратные кольцевые буфера" и чем они отличаются от address mode осталось тайной(:-).
--------------------
Делай сразу хорошо, плохо само получится
|
|
|
|
|
Aug 28 2007, 18:33
|

Знающий
   
Группа: Свой
Сообщений: 902
Регистрация: 2-01-06
Из: Краснодар
Пользователь №: 12 768

|
Цитата(=GM= @ Aug 28 2007, 20:02)  Привет всем! Отъезжал ненадолго, тут праздник местный был (summer bank holiday), теперь будем смотреть ответы и отвечать по мере сил-возможностей(:-)
1) To SasaVitebsk. Как всегда, приведен добротный код. Есть только мелкие вопросики. Фрагмент ниже непонятен, вроде бы надо буфер разместить в озу, а у вас стоит пзу.
Трудно разобраться с передачей, метка outwl указана дважды, а метки outwlsZ нет вовсе.
2) Как обычно ничем не удивил и не порадовал широкоизвестный в узких кругах эникейщик =AVR=, вроде бы и похвалил, вроде бы и пожурил в одно и то же время, но что такое "аппаратные кольцевые буфера" и чем они отличаются от address mode осталось тайной(:-). Господин учитель информатики раздает слонов за итоговую работу по теме "кольцевые буферы". Вы бородку клинышком и пенсне не носите случаем?Если нет-подумайте на эту тему,имхо вам должно пойти.
--------------------
"Hello, word!" - 17 errors 56 warnings
|
|
|
|
|
Aug 28 2007, 19:20
|
Гуру
     
Группа: Свой
Сообщений: 2 712
Регистрация: 28-11-05
Из: Беларусь, Витебск, Строителей 18-4-220
Пользователь №: 11 521

|
Цитата(=GM= @ Aug 28 2007, 19:02)  1) To SasaVitebsk. Как всегда, приведен добротный код. Есть только мелкие вопросики. Фрагмент ниже непонятен, вроде бы надо буфер разместить в озу, а у вас стоит пзу. Да. Я так и написал. Просто вырвал кусочек использованный для объявления таблицы автоматически выровненной на границу. Что-нибудь подобное можно и для озу придумать. Чтобы вместо ORG.  Цитата Трудно разобраться с передачей, метка outwl указана дважды, а метки outwlsZ нет вовсе. Метка outwl указана 1 раз и является вызываемой. Второй раз использована метка outw1. Просто на том шрифте этого не видно. Если текст перенесёте, то увидите. Написано это где-то в 1994-95 примерно. Я тогда так принял локальные метки изменять для себя 1,2,3... Короче что-то по типу символа "_" у defunct. Кстати применение символа "_" используется некоторыми компиляторами для организации локальных меток. Иногда при макросах также делается. Так что наезд на defunct некорректен.  Считаю его подход правильным. Просто когда я писал ещё не сложилось ничего (у меня). Да и вообще человек постоянно меняется оставаясь самим собой. Наверное было бы любопытно поболтать с собой самим лет 20 тому назад. Ну, безусловно избегая фраз типа: "придурок правее бери". А что не хватает, - так ведь выхвачено было по живому. Оно же там всё повязано.  Там нет ничего любопытного. Код ;**************************************************************** ;* Вспомогательная. Выводит в буфер вывода цифру согласно би- * ;* ту Т. Потом выводит "пробел". * ;* Портится tmph,wl и Z. * ;****************************************************************
outwlt: clr wl bld wl, 0
;**************************************************************** ;* Вспомогательная. Выводит в буфер вывода цифру согласно ре- * ;* гистра wl. Потом выводит "пробел". * ;* Портится tmph,wl и Z. * ;****************************************************************
outwlr: andi wl, 3 ori wl, $30 rcall outwl ldi wl, $20
;**************************************************************** ;* Вывод символа (wl) в буфер вывода. При выводе контроли- * ;* руется заполнение буфера. При переполнении буфера вывод тор- * ;* мозится, и подпрограмма не завершится, пока он не закончится.* ;* Две точки входа: outwl и outwlsZ; * ;* Портятся tmph, Z. (для outwl) * ;* Портятся tmph. (для outwlsZ) * ;****************************************************************
outwl: mov tmph, TBH sub tmph, TBE brcc outw1 subi tmph, -lBuf outw1: cpi tmph, 2 breq outwl mov Zl, TBE ldi ZH, high(TxBuf) st Z+, wl cpi Zl, lBuf brne outw2 clr Zl outw2: mov TBE, Zl ret
outwlsZ: push Zl push Zh rcall outwl pop Zh pop Zl ret
|
|
|
|
Guest_=AVR=_*
|
Aug 28 2007, 20:21
|
Guests

|
Аппаратные буфера отличаются от address mode тем же, чем summer bank holiday от аристократической и загадочной команды cbr xl,0xE0. То есть, простите, Sir, от плебейской и понятной команды andi xl,0x1F - ну, Вы, смею надеяться, поняли, а остальным не обязательно, а то опять засмеют Вас, как тогда с этим 24-канальным ШИМом - помните? Ну и славненько. И аппаратные кольцевые буфера, Sir, в DSP не делаются, а имеются - вернее, имеется их поддержка, которая активируется установкой битиков в соответствующих регистриках - размерчик там буферочка, направление заполнения, флажочки разные удобные - если маразм склероз замучил, то прочитайте DS на какой-нибудь DSP или даже, не побоюсь этого слова, dsPIC - там Вам постараются объяснить это два раза и медленно, тщательно проговаривая слова. Нам, эникейщикам, в наших узких кругах такое практически недоступно - все урывками да слухами, никакого, панимаишь, информационного бума - один свист. Кстати, не Вы ли свистели, эсквайр? Ай-я-яй, как некультурно!  to SasaVitebsk: .org прекрасно и штатно работает и в ОЗУ, просто для этого нужно использовать его в секциях данных - dseg и/или eseg, Position Counter (PC) у каждой секции свой, и считает такой PC те единицы размещения, которые применяются в данной секции - слова для .code, байты для .dseg и .eseg. Выравнивание по кратной границе удобно делать при помощи (моего) макроса align (если директивы align нет в соответствующем ассемблере): Код ; For AVR Assembler 2 only .macro align .org @0+PC-PC%@0 .endm .dseg align (32+SRAM_START) ringb1: .byte 32; @ 0x0080 ringb2: .byte 32; @ 0x00A0 При размещении буферов в ОЗУ по нужным границам надо не забывать о том, что ОЗУ в разных АВР начинается с разных адресов - где 0x0060, а где и 0x0100. В примере выше адрес начала ОЗУ (SRAM_START, значение определено в .inc-файле) использовано как добавка к параметру align
|
|
|
|
|
Aug 28 2007, 22:33
|

Ambidexter
    
Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282

|
Цитата(WHALE @ Aug 28 2007, 18:33)  Господин учитель информатики раздает слонов за итоговую работу по теме "кольцевые буферы". Вы бородку клинышком и пенсне не носите случаем? Если нет-подумайте на эту тему, имхо вам должно пойти. Хорошо бы вам, уважаемый, не шипеть злобно из-за угла, а сказать что-нибудь ближе к теме ветки, не отклоняясь. Если, конечно, есть чем поделиться. На тему пресловутых циклических буферов, конечно, а не на тему пенсне. Цитата(=AVR= @ Aug 28 2007, 20:21)  Аппаратные буфера отличаются от address mode тем же, чем summer bank holiday от аристократической и загадочной команды cbr xl,0xE0. То есть, простите, Sir, от плебейской и понятной команды andi xl,0x1F - ну, Вы, смею надеяться, поняли, а остальным не обязательно, а то опять засмеют Вас, как тогда с этим 24-канальным ШИМом - помните? Режим косвенной циклической адресации (indirect circular addressing mode) именно в дсп я знаю хорошо, поскольку каждый божий день применяю: и такой movl *ar6%++,acc и такой movl *+xar6[ar1%++] А вот аппаратного буфера не нахожу, да, склероз-не склероз, свисти-не свисти...нету такого! Эникейщики, они тем и отличаются от обычных кодеров, что неглубоко копают, помните? А ещё они любят навести тень на плетень, сравнить, например, summer bank holiday с командой cbr. Что касаемо 24-канального программного шима, не надо ля-ля, ваша программа проиграла моей программе по скорости более, чем в ТРИ раза. И никто не смеялся, один вы орали и брызгали слюной, всех задолбали, прямо скажем.
--------------------
Делай сразу хорошо, плохо само получится
|
|
|
|
|
Aug 29 2007, 00:07
|

Ambidexter
    
Группа: Свой
Сообщений: 1 589
Регистрация: 22-06-06
Из: Oxford, UK
Пользователь №: 18 282

|
Цитата(zltigo @ Aug 28 2007, 22:41)  Если уже начали о софтовых кольцевых буферах отдельную тему, то следует как минимум раскрыть тему пошире: -получении информации о свободном месте в буфере; -различных стратегиях поведения при переполнении буфера; -организации небайтовых буферов; Ищу решение, пока красивого нет. Нашёл вот только наиболее короткий способ чтения из буфера и записи в буфер практически ПРОИЗВОЛЬНОГО размера. Скажем для тайни – от 1 до 64 байт. Для старших моделей 1-256. По-моему, довольно симпатично(:-). Идея состоит в том, чтобы адрес конца буфера помещался непосредственно перед границей по модулю 2^N. В то же время длина буфера может быть произвольной в пределах этого модуля 2^N. Например, конец буфера для тайни нужно выбрать равным 0xBF. Тогда начало буфера можно выбирать любое в пределах 0x80-0xBE. Полные фрагменты кода на запись/чтение в/из циклического буфера приведены ниже. Фрагменты похожи на реализацию, показанную mse (см. пост #5), но короче на одно слово. А. Запись байта из регистра data в циклический буфер buffer Код bwrite: lds xl,head ;указатель clr xh ;на запись st x+,data ;запишем байт sbrc xl,6 ;конец буфера? ldi xl,low(buffer);да, установим начало sts head,xl ;новый указатель Б. Чтение байта из циклического буфера buffer в регистр data Код bread: lds xl,tail ;указатель clr xh ;на чтение ld data,x+ ;прочитаем байт sbrc xl,6 ;конец буфера? ldi xl,low(buffer);да, установим начало sts tail,xl ;новый указатель
--------------------
Делай сразу хорошо, плохо само получится
|
|
|
|
|
  |
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|