Перейти к содержанию
    

Циклический буфер на AVR

Тема открыта по горячим следам недавней острой дискуссии.

 

В ней одним из участников (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 – регистровые переменные, содержащие адреса-указатели ячеек в озу для записи, чтения и конца буфера соответственно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если использовать буфер не произвольной длины, а кратный степени 2, и размещать его в памяти не произвольно, а начиная с адресов, кратных длине буфера, то размер кода можно немного сократить.

Если вы хотели услышать отзыв о вашем способе, то вот он:

Хороший способ. Особо хорошо подходит для задач где требуется выделить некий "под-поток" внутри потока. Другими словами для организации "probation" периода.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Гость =AVR=

Такой метод (посредством урезания операцией "AND") организации кольцевых буферов с длиной и начальным адресом, кратными 2^N, весьма удобен и эффективен - недаром он широко применяется аж с 80-х годов. Эффективнее него - только аппаратные кольцевые буфера, реализованные в некоторых DSP, а также в dsPIC.

 

=GM= молодец - очень полезно время от времени напоминать широким массам о том, что Волга впадает именно в Каспийское море, а не в Северное :)

Изменено пользователем =AVR=

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

IMHO, обсуждение циклических буферов в привязке к архитектуре имеет смысл только при использовании ассемблера и всвязи с нюансами соблюдения атомарности операций. Ну и, как заметил =AVR=, при наличии аппаратной реализации в соответствующих камнях.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

      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
    ...

Вроде нигде не накосячил...А?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Такой метод (посредством урезания операцией "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 года последние изменения. Сейчас вообще буфер большой. В байт не влазит. На мелочи не обращать внимание просто по живому вырезалось.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Доброго дня!

 

Мне вот очень интересно применение кольцевых буферов для реализации консоли.

Со временем "написались" некоторые участки кода, которые позволяют в общем виде сделать реакцию на команды. Юзаю повсеместно. Хотелось бы услышать отзывы и предложения по данной проблеме. :-)

command.rar

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Привет всем! Отъезжал ненадолго, тут праздник местный был (summer bank holiday), теперь будем смотреть ответы и отвечать по мере сил-возможностей(:-)

 

1) To SasaVitebsk. Как всегда, приведен добротный код. Есть только мелкие вопросики. Фрагмент ниже непонятен, вроде бы надо буфер разместить в озу, а у вас стоит пзу.

Кстати возможно автоматическое размещение буфера на границу по типу применяемой мной привязки таблицы данных

    .cseg
    .org    (pc & $ff80)+$80

Трудно разобраться с передачей, метка outwl указана дважды, а метки outwlsZ нет вовсе.

;*   Две точки входа: 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 осталось тайной(:-).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Привет всем! Отъезжал ненадолго, тут праздник местный был (summer bank holiday), теперь будем смотреть ответы и отвечать по мере сил-возможностей(:-)

 

1) To SasaVitebsk. Как всегда, приведен добротный код. Есть только мелкие вопросики. Фрагмент ниже непонятен, вроде бы надо буфер разместить в озу, а у вас стоит пзу.

 

Трудно разобраться с передачей, метка outwl указана дважды, а метки outwlsZ нет вовсе.

 

2) Как обычно ничем не удивил и не порадовал широкоизвестный в узких кругах эникейщик =AVR=, вроде бы и похвалил, вроде бы и пожурил в одно и то же время, но что такое "аппаратные кольцевые буфера" и чем они отличаются от address mode осталось тайной(:-).

 

Господин учитель информатики раздает слонов за итоговую работу по теме "кольцевые буферы".

Вы бородку клинышком и пенсне не носите случаем?Если нет-подумайте на эту тему,имхо вам должно

пойти.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1) To SasaVitebsk. Как всегда, приведен добротный код. Есть только мелкие вопросики. Фрагмент ниже непонятен, вроде бы надо буфер разместить в озу, а у вас стоит пзу.

Да. Я так и написал. Просто вырвал кусочек использованный для объявления таблицы автоматически выровненной на границу. Что-нибудь подобное можно и для озу придумать. Чтобы вместо ORG. :)

Трудно разобраться с передачей, метка outwl указана дважды, а метки outwlsZ нет вовсе.

Метка outwl указана 1 раз и является вызываемой. Второй раз использована метка outw1. Просто на том шрифте этого не видно. Если текст перенесёте, то увидите. Написано это где-то в 1994-95 примерно. Я тогда так принял локальные метки изменять для себя 1,2,3... Короче что-то по типу символа "_" у defunct. Кстати применение символа "_" используется некоторыми компиляторами для организации локальных меток. Иногда при макросах также делается. Так что наезд на defunct некорректен. :) Считаю его подход правильным. Просто когда я писал ещё не сложилось ничего (у меня). Да и вообще человек постоянно меняется оставаясь самим собой. Наверное было бы любопытно поболтать с собой самим лет 20 тому назад. Ну, безусловно избегая фраз типа: "придурок правее бери". :biggrin:

 

А что не хватает, - так ведь выхвачено было по живому. Оно же там всё повязано. :) Там нет ничего любопытного.

 

;****************************************************************
;*   Вспомогательная. Выводит в буфер вывода цифру согласно би-    *
;* ту Т. Потом выводит "пробел".                *
;*   Портится 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Гость =AVR=

Аппаратные буфера отличаются от 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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Господин учитель информатики раздает слонов за итоговую работу по теме "кольцевые буферы". Вы бородку клинышком и пенсне не носите случаем? Если нет-подумайте на эту тему, имхо вам должно пойти.

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

 

Аппаратные буфера отличаются от 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-канального программного шима, не надо ля-ля, ваша программа проиграла моей программе по скорости более, чем в ТРИ раза. И никто не смеялся, один вы орали и брызгали слюной, всех задолбали, прямо скажем.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Moderator:

Так, оба двое и присоединившийся к ним совcем уж не по делу WHALE - снижаем накал страстей до уровня соответствующего технической теме а не банальной перепалке во freetalk.

 

Если уже начали о софтовых кольцевых буферах отдельную тему, то следует как минимум раскрыть тему пошире:

-получении информации о свободном месте в буфере;

-различных стратегиях поведения при переполнении буфера;

-организации небайтовых буферов;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Если уже начали о софтовых кольцевых буферах отдельную тему, то следует как минимум раскрыть тему пошире:

-получении информации о свободном месте в буфере;

-различных стратегиях поведения при переполнении буфера;

-организации небайтовых буферов;

Ищу решение, пока красивого нет. Нашёл вот только наиболее короткий способ чтения из буфера и записи в буфер практически ПРОИЗВОЛЬНОГО размера. Скажем для тайни – от 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       ;новый указатель

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 AVR спасибо. До вашего варианта не додумался. Про DSEG, естественно, знаю. Последнее время пишу всё на Си. Думаю что ваш макрос будет полезен многим. Интересные находки всегда любопытны. Я вот всё больше и больше склоняюсь к мысли что необходимо унифицировать некоторые узлы и блоки, процедуры процессоронезависимые и даже переменные и структуры.

 

Прошу прощения за отход от темы может кто посоветует хорошую книгу в данной области. То есть что-то по типу визуализации/структурирования. Даже не знаю как назвать эту область. Короче выработки единого подхода к написанию прог на Си.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...