AK Laboratory

Электроника и радиотехника => Цифровая техника => Тема начата: Shaman от 07 Окт., 2020, 21:51

Название: Отладчик avr-gdb и эмулятор simulavr
Отправлено: Shaman от 07 Окт., 2020, 21:51
У меня Linux головного мозга.
Поэтому когда я решил сделать свой космический модуль (https://anklab.ru/forum/index.php?topic=45.0) то стал использовать средства разработки по эти ОС, тем паче их в достатке.
Но при отладке возникла проблема, следующего характера: на макетке я могу увидеть только финальный этап работы программы, а если до него даже не дошло то как определить где затык?

Для форточек существует среда разработки, где это все реализованно, но ... Вобчем я приступил к поиску и нашел (https://www.nongnu.org/simulavr/).

Это эмулятор микроконтроллеров серии AVR.

Алгоритм работы следующий:
1. Запускаем сам эмулятор командой.
simulavr -g -p 1200 -d atmega328 -F 8000000

где: -g запустить эмулятор как сервер куда будет подключаться отладчик, -p порт сервера, -d atmega328 выбор эмулируемого контроллера, -F частота работы контроллера в герцах.

2. Комилируем прошивку с флагом -ggdb чтобы в ней сохранилась отладочная информация.

3. Запускаем отладчик выбрав файл прошивки.
avr-gdb ./avrblink.o
Здесь листинг прошивки для понимания
#define F_CPU 8000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <string.h>
#define _data_1 PORTC |= (1<<3)      // Выбираем пин для вывода данных в сдвиговые регистры и устанавливаем на нём 1 путём установки порта и назначения разряда соответствующего пину
#define _data_0 PORTC &= ~(1<<3)    // -.- устанавливаем на нём 0 -.-
#define _clock_high PORTC |= (1<<4)  // Выбираем пин clock и устанавливаем на нём высокий уровень
#define _clock_low PORTC &= ~(1<<4)  // Выбираем пин clock и устанавливаем на нём низкий уровень
#define _latch_up PORTC |= (1<<5)    // Выбираем пин защёлки и поднимаем её
#define _latch_down PORTC &= ~(1<<5) // Выбираем пин защёлки и опускаем её
#define SIZE_SCREEN 4                //размер буфера экрана
#define SIZE_ABC 8                  //размер буфера экрана
unsigned char buffer [SIZE_SCREEN];  //создаем буфер буфер экрана
// присваиваем имя значению в буфере экрана оно означает цвет светодиода
#define _Blue buffer[3]              
#define _Green buffer[2]
#define _Red buffer[1]
#define _RGB buffer[0]
#define _Null memset (buffer, 0, 4)  //обнуление буфера экрана

//Алфавит
unsigned char ABC [SIZE_ABC] =      
{ 
        0b00000001, 
        0b00000010, 
        0b00000100, 
        0b00001000, 
        0b00010000, 
        0b00100000, 
        0b01000000, 
        0b10000000,
};
// конец алфавита

int record ();      //прототип функции для записи в сдвиговые регистры
int blink ();      //прототип функции включающей мигалку
int stop ();        //прототип функции включающей сигнал стоп
int left ();        //прототип функции включающей сигнал левого поворота
int right ();      //прототип функции включающей сигнал правого поворота
int left_stop ();  //прототип функции включающей сигнал левого поворота и сигнала стоп
int right_stop ();  //прототип функции включающей сигнал правого поворота и сигнала стоп

int main (void) 
{

    DDRC = 0b00111000; //устанавливаем необходимые пины на вывод
    _clock_high;      // для предотвращения ложного срабатывания
    _latch_up;        // поднимаем защелку 
    
    while (1)
    {
        blink ();
        _delay_ms(50);
        stop ();
        _delay_ms(50);
        left ();
        _delay_ms(50);
        right ();
        _delay_ms(50);
        left_stop ();
        _delay_ms(50);
        right_stop ();
        _delay_ms(50);              
    }
  
    
}
int record ()
{
    _latch_down;                          // опускаем защелку
    
  for (int x = 0; x<SIZE_SCREEN; x++)      //цикл для переборки байтов в массиве (уточнить как храниться число)
  {
      for (int y = 7; y>=0; y--)          // цикл для перебора битов в байте и подачи их на выход
        {  
            _clock_low;                    // переводим регистры в режим ожидания данных  
            
            if ((buffer[x] & (1<<y)) == 0) // накладываем побитовую маску для считывания нужного бита
            {
                _data_0;                  // если ответ 0, выставляем на входе регистра 0
            }
            else 
            {
                _data_1;                  //иначе, выставляем на входе регистра 1
            }
            
            _clock_high;                  // записываем данные в регистр
            
        }
  }
    _latch_up;                            // поднимаем защелку, включаем светодиоды
}

int blink ()
{  
    _Null;
    _Red = ABC[7] | ABC[6] | ABC[5] | ABC[4] | ABC[3] | ABC[2] | ABC[1] | ABC[0];
    _RGB = ABC[0];
    record ();
    _delay_ms(50);
    _Null;
    _Blue = ABC[7] | ABC[5]  | ABC[2]  | ABC[0];
    _RGB = ABC[2];
    record ();
}
int stop () 
{
    _Null;
    _Red = ABC[7] | ABC[6] | ABC[5] | ABC[4] | ABC[3] | ABC[2] | ABC[1] | ABC[0];
    _RGB = ABC[0];
    record ();    
}
int left ()
{
    _Null;
    _Green = ABC[4];
    _Red = ABC[4];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[5];
    _Red = ABC[5];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[6];
    _Red = ABC[6];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[7];
    _Red = ABC[7];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[7] | ABC [4];
    _Red = ABC[7] | ABC [4];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[7] | ABC [5];
    _Red = ABC[7] | ABC [5];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[7] | ABC [6];
    _Red = ABC[7] | ABC [6];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[7] | ABC [6] | ABC [4];
    _Red = ABC[7] | ABC [6] | ABC [4];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[7] | ABC [6] | ABC [5];
    _Red = ABC[7] | ABC [6] | ABC [5];
    record ();    
}
int right ()
{
    _Null;
    _Green = ABC[3];
    _Red = ABC[3];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[2];
    _Red = ABC[2];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[1];
    _Red = ABC[1];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[0];
    _Red = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[3] | ABC [0];
    _Red = ABC[3] | ABC [0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[2] | ABC [0];
    _Red = ABC[2] | ABC [0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[1] | ABC [0];
    _Red = ABC[1] | ABC [0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[3] | ABC [1] | ABC [0];
    _Red = ABC[3] | ABC [1] | ABC [0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[2] | ABC [1] | ABC [0];
    _Red = ABC[2] | ABC [1] | ABC [0];
    record ();
}
int left_stop ()
{
    _Null;
    _Green = ABC[4];
    _Red = ABC[4] | ABC[3] | ABC[2] | ABC[1] | ABC[0];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[5];
    _Red = ABC[5] | ABC[3] | ABC[2] | ABC[1] | ABC[0];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[6];
    _Red = ABC[6] | ABC[3] | ABC[2] | ABC[1] | ABC[0];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[7];
    _Red = ABC[7] | ABC[3] | ABC[2] | ABC[1] | ABC[0];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[7] | ABC [4];
    _Red = ABC[7] | ABC [4] | ABC[3] | ABC[2] | ABC[1] | ABC[0];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[7] | ABC [5];
    _Red = ABC[7] | ABC [5] | ABC[3] | ABC[2] | ABC[1] | ABC[0];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[7] | ABC [6];
    _Red = ABC[7] | ABC [6] | ABC[3] | ABC[2] | ABC[1] | ABC[0];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[7] | ABC [6] | ABC [4];
    _Red = ABC[7] | ABC [6] | ABC [4] | ABC[3] | ABC[2] | ABC[1] | ABC[0];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[7] | ABC [6] | ABC [5];
    _Red = ABC[7] | ABC [6] | ABC [5] | ABC[3] | ABC[2] | ABC[1] | ABC[0];
    _RGB = ABC[0];
    record ();
}
int right_stop ()
{
    _Null;
    _Green = ABC[3];
    _Red = ABC[7] | ABC[6] | ABC[5] | ABC[4] | ABC[3];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[2];
    _Red = ABC[7] | ABC[6] | ABC[5] | ABC[4] | ABC[2];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[1];
    _Red = ABC[7] | ABC[6] | ABC[5] | ABC[4] | ABC[1];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[0];
    _Red = ABC[7] | ABC[6] | ABC[5] | ABC[4] | ABC[0];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[3] | ABC [0];
    _Red = ABC[7] | ABC[6] | ABC[5] | ABC[4] | ABC[3] | ABC [0];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[2] | ABC [0];
    _Red = ABC[7] | ABC[6] | ABC[5] | ABC[4] | ABC[2] | ABC [0];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[1] | ABC [0];
    _Red = ABC[7] | ABC[6] | ABC[5] | ABC[4] | ABC[1] | ABC [0];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[3] | ABC [1] | ABC [0];
    _Red = ABC[7] | ABC[6] | ABC[5] | ABC[4] | ABC[3] | ABC [1] | ABC [0];
    _RGB = ABC[0];
    record ();
    _delay_ms(100);
    _Null;
    _Green = ABC[2] | ABC [1] | ABC [0];
    _Red = ABC[7] | ABC[6] | ABC[5] | ABC[4] | ABC[2] | ABC [1] | ABC [0];
    _RGB = ABC[0];
    record ();
}
[свернуть]

4. Подключаемся к серверу.
(gdb) target remote localhost:1200

5. Загружаем прошивку.
(gdb) load

6. Создаём точку останова командой break
break record
Breakpoint 4 at 0xa6: file avrblink.c, line 70.
Где в качестве аргумента можно указать как в моём случае имя функции, так и непосредственно строку программы.
Посмотреть какие есть точки останова можно командой info breakpoint
Удалить delete N/ где N порядковый номер точки останова.

7. В avr-gdb есть 3 основных команды для просмотра значений переменной или области памяти.
x - команда проверяет память, начиная с определенного адреса.
 Пример
x/4t &buffer

где (4) кол-во байт памяти которые необходимо прочитать, (t) вид отображения двоичный, (&buffer) начальный адрес памяти откуда производиться чтение. Может быть указан как напрямую, так и ввиде указателя как в примере. Оператор & вычисляет адрес переменной
[свернуть]
print – выводит значение какого-либо выражения (выражение передаётся в качестве параметра);
display – добавляет выражение в список выражений, значения которых отображаются каждый раз при остановке программы; Здесь подробнее.
Поскольку мне нужно было смотреть как изменяестя буфер экрана после каждой точки отанова я использавал команду display
display/t buffer

8. Продолжаем выполнение программы командой continue. Порограмма будет выполнятся до следующей точки останова и по достижени оной в консоль будет выводиться значение буфера экрана
/t buffer = {1, 1001111, 1000000, 0}
команды для пошагового выполнения программы
step – или s пошаговое выполнение программы;
next – или n пошаговое выполнение программы, но, в отличие от команды step, не выполняет пошагово вызываемые функции;
finish – выполняет программу до выхода из текущей функции; отображает возвращаемое значение,если такое имеется;
[свернуть]
Вот здесь описание других команд и естественно есть справка в самой программе.
Мне этого пока достаточно. Интернеты говорят о режиме Emacs GUD, это режим для отладки с помощью avr-gdb в Emacs... по мимо регистров и локальных переменных используемых в программе, он позволяет просматривать участки памяти... и прочие вкусности, но я пока не разбирался.

P.S. Если кому то помог то замечательно, если кто-то подскажет как ещё можно использовать этот софт или даст ссылку на инструкцию по использованию Emacs GUD, буду рад.

P.P.S Насколько я знаю через AVR-Studio можно пошагово исполнять программу в железе интересно это можно реализовать в Linux?

Название: Re: Отладчик avr-gdb и эмулятор simulavr
Отправлено: zenon от 07 Окт., 2020, 22:17
Как нибудь попробую, до emacs так и не дорос, но честно говоря он мне не по душе, сколько раз пытался, но безрезультатно.
Так что под линукс для себя понял, только хардкор - только vim.
Конечно плюшки в нём настраиваются непривычно, порой долго, но уже свыкся.
Из IDE понравилась Platformio, но забросил.
BluePill stm32 с ардуиновским бутлоадером запускал с помощью Platformio, в качестве редактора используя vim, где-то на своём сайте (сейчас лежит, сервер сдох) даже про это писал кажется.
+++
На данном этапе сижу под оффтопиком, потому как нужен совсем другой софт.
ы. В дуалбуте gentoo, последний раз обновлял бедную год назад... :)
Название: Re: Отладчик avr-gdb и эмулятор simulavr
Отправлено: Shaman от 08 Окт., 2020, 18:04
Цитата: zenon от 07 Окт., 2020, 22:17в качестве редактора используя vim
Я так и не смог себя заставить в нём разобраться  :) Отталкивает с самого начала, редактор должен редактировать после запуска, а не заставлять запоминать комбинации клавиш для сего действа. Возможно я слишком молод для понимания этой идеологии  ;D
Название: Re: Отладчик avr-gdb и эмулятор simulavr
Отправлено: zenon от 08 Окт., 2020, 18:40
Я тоже не ас в vim, эволюция nano -> vim была долгой. Bash -> zsh было быстрее.
.vimrc в котором это видно, Ctrl+O, Ctrl+X сохранить, выйти.
syntax enable
set background=dark
" colorscheme grb256 "" colorscheme distinguished "" colorscheme jellybeans " colorscheme railscasts
" badwolf  codeschool    github jellybeans solarized  vividchalk
" candy distinguished  grb256  ir_black  railscasts  twilight   vwilight

" BEST - vwlight/codeschool/jellybeans
" colorscheme jellybeans
colorscheme diokai

" au BufReadPost *.conf set syntax=etc


let g:lastplace_ignore = "gitcommit,gitrebase,svn,hgcommit"
let g:lastplace_ignore_buftype = "quickfix,nofile,help"
let g:lastplace_open_folds = 0

set autoread        " Auto-reload buffers when file changed on disk
" Allow backgrounding buffers without writing them, and remember marks/undo
" for backgrounded buffers
set hidden         " Disable swap files; systems don't crash that often these days
set updatecount=0

set nocompatible    " Вообще, первая команда, которую надо поставить в vim — set nocompatible и отключить (нужную только олдовым хакерам BSD 4.4-Lite) совместимость с vi Билла Джоя. Он сразу станет удобней работать.
" nocompatible устанавливается автоматически, если есть .vimrc или .gvimrc.
" Для опции backspace я бы указал set backspace=indent,eol,start, так правильнее, нагляднее и понятнее, поддержка set backspace=2 оставлена из соображений совместимости.
" set backspace=2     "— что бы нажатие клавиши Backspace и через конец строки и отступы.
set wrapmargin=5    "— отступ от правой границы окна, где надо начинать перенос. Удобнее textwidth, если размеры окна изменяются.
set tildeop "~"       " в vim используется для изменения регистра текущего символа. Строго говоря, эта операция нарушает идеологию vi о том, что для каждой операции можно добавлять movement. Эта опция включает такую возможность, теперь, например, "~W" изменит регистр до конца слова.
set scrolloff=3     "— сколько строк вверху и внизу экрана показывать при скроллинге. Очень удобно.
set sidescrolloff=2 "

" Whitespace
set tabstop=2                                        " количество пробелов в табуляции
set shiftwidth=2                                     " an autoindent (with <<) is two spaces
set expandtab                                        " use spaces, not tabs
set smarttab                                         "
set et                                               " включим автозамену по умолчанию 
set nowrap                                           " попросим Vim не-переносить длинные строки
set ai                                               " включим автоотступы для новых строк
set cin                                              " включим отступы в стиле Си
set listchars=tab:▸\ ,trail:•,extends:❯,precedes:❮   " Indicator chars    set listchars=tab:··¶ set listchars=tab:»\ ,trail:·,eol:¶¶
set list                                             " показывать непечатаемые символы
set showbreak=↪\•
set backspace=indent,eol,start                       " backspace through everything in insert mode

" Joining lines
if v:version > 703 || v:version == 703 && has("patch541")
   set formatoptions+=j            " Delete comment char when joining commented lines
endif
set nojoinspaces                  " Use only 1 space after "." when joining lines, not 2


set synmaxcol=800  " don't try to highlight long lines
set nonumber       " line numbers aren't needed
set ruler          " show the cursor position all the time
set cursorline     " highlight the line of the cursor
set showcmd        " show partial commands below the status line
set shell=bash     " avoids munging PATH under zsh
"let g:is_bash=1    " default shell syntax
set history=200    " remember more Ex commands


" Time out on key codes but not mappings.
" Basically this makes terminal Vim work sanely.
set notimeout
set ttimeout
set ttimeoutlen=100



" Далее настроим поиск и подсветку результатов поиска и совпадения скобок
set showmatch
set hlsearch
set incsearch
set ignorecase
set lz " ленивая перерисовка экрана при выполнении скриптов


" Порядок применения кодировок и формата файлов
set ffs=unix,dos,mac
set fencs=utf-8,cp1251,koi8-r,ucs-2,cp866

" Взаимодействие и элементы интерфейса
" Я часто выделяю мышкой содержимое экрана в Putty, но перехват мышки в Vim мне иногда мешает. 
" Отключаем функционал вне графического режима:
if !has('gui_running')
   set mouse= 
endif

set wildmenu " — менюшки в консольке
" Использование иксового клипборда:
set clipboard+=unnamed

" set paste     " При копипасте (например Ctrl+Shift+V) корректно проставляются все отступы
" set pastetoggle=
" set number
" Если всё поехало вкривь после вставки - нажать Ctrl+u
inoremap <silent> <C-u> <ESC>u:set paste<CR>.:set nopaste<CR>gi

" set statusline=%F%m%r%h%w\ [FORMAT=%{&ff}]\ [TYPE=%Y]\ [ASCII=\%03.3b]\ [HEX=\%02.2B]\ [POS=%04l,%04v][%p%%]\ [LEN=%L]
" set statusline=%t\ %y%m%r[%{&fileencoding}]%<[%{strftime(\"%d.%m.%y\",getftime(expand(\"%:p\")))}]%k%=%-14.(%l,%c%V%)\ 

if has("statusline") && !&cp
  set laststatus=2                   " always show the status bar
  set statusline=%<%1*\ %f\ %*       " filename
  set statusline+=%2*%m%r%*          " modified, readonly
  set statusline+=\ %3*%y%*          " filetype
  set statusline+=\ %4*%{fugitive#head()}%0*
  set statusline+=%=                 " left-right separation point
  set statusline+=\ %5*%l%*/%L[%p%%] " current line/total lines
  set statusline+=\ %5*%v%*[0x%B]    " current column [hex char]
endif

hi StatusLine term=inverse,bold cterm=NONE ctermbg=24 ctermfg=189
hi StatusLineNC term=inverse,bold cterm=NONE ctermbg=24 ctermfg=153
hi User1 term=inverse,bold cterm=NONE ctermbg=29 ctermfg=159
hi User2 term=inverse,bold cterm=NONE ctermbg=29 ctermfg=16
hi User3 term=inverse,bold cterm=NONE ctermbg=24
hi User4 term=inverse,bold cterm=NONE ctermbg=24 ctermfg=221
hi User5 term=inverse,bold cterm=NONE ctermbg=24 ctermfg=209





"  Mapping
"  автодополнение фигурной скобки (так, как я люблю :)
imap {<CR> {<CR>}<Esc>O<Tab>

" автодополнение по Control+Space
imap <C-Space> <C-N>
" 'умный' Home
nmap <Home> ^
imap <Home> <Esc>I

" выход
imap <C-x> <Esc>:qa<CR>
nmap <C-x> :qa<CR>
" сохранение
imap <C-o> <Esc>:w<CR>
nmap <C-o> :w<CR>
"imap <C-s> <Esc>:w<CR>
"nmap <C-s> :w<CR>

" no search 
nmap <silent> <F3> :silent nohlsearch<CR>
imap <silent> <F3> <C-o>:silent nohlsearch<CR>

" следующая ошибка
imap <C-F10> <Esc>:cn<CR>i
nmap <C-F10> :cn<CR>

" предыдущая ошибка
imap <S-F10> <Esc>:cp<CR>i
nmap <S-F10> :cp<CR>

" вкл/выкл отображения номеров строк
imap <C-F1> <Esc>:set<Space>nu!<CR>a
nmap <C-F1> :set<Space>nu!<CR>

" вкл/выкл отображения найденных соответствий
imap <S-F1> <Esc>:set<Space>hls!<CR>a
nmap <S-F1> :set<Space>hls!<CR>

" q: sucks
nmap q: :q

Но два режима работы vim надо всё-таки понять.
Сборку, компиляцию, прошивку, выхлоп ошибок прикручивается не очень сложно.
Название: Re: Отладчик avr-gdb и эмулятор simulavr
Отправлено: Shaman от 08 Окт., 2020, 21:45
Вух. Благодарю. Оставлю на будущее. Пока связки nano и bach мне достаточно. Я в самом начале изучения программирования чего-либо и автомобиль пока не нужен достаточно велосипеда.  :)  И поскольку писать софт для серверов без Иксов пока не нужно, обхожусь связкой Visual Studio Code и AVR8_Burn-O-Mat для прошивки.
Название: Re: Отладчик avr-gdb и эмулятор simulavr
Отправлено: zenon от 08 Окт., 2020, 22:41
Ну так если с VS знаком, то platformio это надстройка над, те как расширение https://platformio.org/platformio-ide
В линуксе оно нативно кажется ставится, уже не помню точно. https://docs.platformio.org/en/latest/core/installation.html
Сейчас пытаюсь в Geany всё делать, отладка пока под вопросом...
Тыц (https://ph0en1x.net/76-howto-config-geany-linux-for-avr-programming-gcc-asm.html)
Там без Makefile, но с ним удобнее, сразу компиляция, сборка, заливка в мк, Shift+F9, F9, F5 например, примеры в соседней ветки выкладывал.