Главная arrow Программирование arrow AVR arrow IAR Embedded Workbench IDE, использование ассемблера в C-проекте Tuesday, May 30 2017  
ГлавнаяКонтактыАдминистрированиеПрограммированиеСсылки
UK-flag-ico.png English Version
GERMAN-flag-ico.png Die deutsche Version
map.gif карта сайта
нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.

Поделиться:

IAR Embedded Workbench IDE, использование ассемблера в C-проекте Версия для печати
Написал microsin   
23.04.2007

1. Обычно необходимость использовать ассемблер бывает в обработчиках прерываний. Для этого лучше их поместить в отдельный файл - меню File\New\File, вставляем текст:
#pragma vector=TIMER0_OVF_vect
__interrupt void TIMER0_OVF_routineTIMER0_OVF_routine(void)
{
   OCR0 = ampl1;
   if (idx < 64)
     ampl1 = SINUS[idx];
   else if (idx < 128)
     ampl1 = SINUS[127-idx];
   else if (idx < 192)
     ampl1 = 255 - SINUS[idx-128];
   else
     ampl1 = 255 - SINUS[255-idx];
   idx = fi1 >> 8;
   fi1 += freq1;   
}
   Сохраняем файл как int.c.

2. Правая кнопка на корневой папке проекта в браузере "Workspace", Add\Add Files... выбираем файл int.c.

3. Правая кнопка на int.c, Options..., ставим галку на Override inherited settings. Идем на закладку List, ставим галки на Output assembler file и на Include source. После перекомпиляции проекта появится ассемблерный файл Debug\List\int.s90. Разберем его содержимое.

4. В начале файла находится заголовок, где указана командная строка для компилятора C:\Program Files\IAR Embedded Workbench 4.0\avr\bin\iccavr.exe:
int.c
--cpu=m16
-mt
-o c:\MyDoc\FLOPPI\ANOTHER\avr\BLDC\Debug\Obj\
-lB c:\MyDoc\FLOPPI\ANOTHER\avr\BLDC\Debug\List\
--initializers_in_flash
-z2
--no_cse
--no_inline
--no_code_motion
--no_cross_call
--no_clustering
--no_tbaa
--debug
-e
-I "C:\Program Files\IAR AVR Embedded Workbench 4.0\avr\INC\"
-I "C:\Program Files\IAR AVR Embedded Workbench 4.0\avr\INC\CLIB\"
--eeprom_size 512

5. Рассмотрим опции компилятора поподробнее. Это поможет разобраться в происходящем и далее компилировать c-файлы отдельно, без IDE.
int.c это понятно, входной файл
--cpu=m16 тип проца
-mt memory model (t=tiny)
-o c:\MyDoc\FLOPPI\ANOTHER\avr\BLDC\Debug\Obj\ указывает, куда поместить объектный файл int.r90
-lB c:\MyDoc\FLOPPI\ANOTHER\avr\BLDC\Debug\List\ указывает, куда поместить list-файл. Это и есть наш текст на ассемблере. Буква B включает добавление в комментариях строк кода C исходника
--initializers_in_flash Aggregate initializers are placed in flash memory
-z2 Оптимизация по size на уровне 2 (отладка)
--no_cse Disable common sub-expression elimination
--no_inline Disable function inlining
--no_code_motion Disable code motion
--no_cross_call Disable cross call optimization
--no_clustering Disable variable clustering
--no_tbaa Disable type based alias analysis
--debug Insert debug info in object file
-e Enable IAR C/C++ language extensions
-I "C:\Program Files\IAR AVR Embedded Workbench 4.0\avr\INC\" Add #include search directory
-I "C:\Program Files\IAR AVR Embedded Workbench 4.0\avr\INC\CLIB\" Add #include search directory
--eeprom_size 512 The size of the inbuilt eeprom area

6. Для того, чтобы разобраться в ассемблерном файле, понадобится следующая документация:
   - datasheet на процессор ATmega16, http://atmel.com/dyn/resources/prod_documents/doc2466.pdf
   - описание сегментов памяти (CSTACK, RSTACK, ..) можно найти в файле http://www.atmel.com/dyn/resources/prod_documents/doc1079.pdf (AVR032: Linker Command Files for the IAR ICCA90 Compiler).
   - AVR IAR Assembler Reference Guide, C:\Program Files\IAR Embedded Workbench 4.0\avr\doc\EWAVR_AssemblerReference.pdf

   Итак, получился из исходника int.c такой выходной текст ассемблера:
       NAME int     ;дает имя программному модулю для дальнейших ссылок на него линкером

       RSEG CSTACK:DATA:NOROOT(0)     ;начало перемещаемого (RSEG) сегмента CSTACK (Data stack),
                                       ; тип сегмента DATA, NOROOT означает,
                                       ; что линкер может отбросить сегмент в случае отсутствия на него ссылок.
                                       ; (0) означает величину выравнивания по адресу, равную 1
                                       ; (2 в степени 0 равно 1), то есть фактически никакого выравнивания нет.
       RSEG RSTACK:DATA:NOROOT(0)     ;то же самое, сегмент RSTACK (return stack)

       EXTERN ?need_segment_init      ;импортирует внешний символ ?need_segment_init

       PUBWEAK `?<Segment init: TINY_Z>`  ;опубликовывает символ `?<Segment init: TINY_Z>`.
                                           ; PUBWEAK отличается от PUBLIC тем, что позволяет публикацию
                                           ;того же символа в разных модулях
       PUBWEAK `??TIMER0_OVF_routineTIMER0_OVF_routine??INTVEC `
       PUBLIC TIMER0_OVF_routineTIMER0_OVF_routine      ;публикует имя обработчика прерывания таймера
       PUBWEAK _A_OCR0                                ;публикует имя переменной _A_OCR0
                                                       ; (регистр OCR0 - Timer/Counter0 Output Compare Register)
       PUBWEAK __?EEARH                               ;публикует имя регистра адреса EEPROM
       PUBWEAK __?EEARL                               ;публикует имя регистра адреса EEPROM
       PUBWEAK __?EECR                                ; ..          EEPROM Control Register
       PUBWEAK __?EEDR                                ; ..          EEPROM Data Register
       PUBLIC ampl1                                   ;тут публикуются переменные (зачем?)
       PUBLIC fi1                                     ;
       PUBLIC freq1                                   ;
       PUBLIC idx                                     ;

TIMER0_OVF_routineTIMER0_OVF_routine SYMBOL "TIMER0_OVF_routineTIMER0_OVF_routine"
`??TIMER0_OVF_routineTIMER0_OVF_routine??INTVEC ` SYMBOL "??INTVEC 36", TIMER0_OVF_routineTIMER0_OVF_routine

       EXTERN SINUS

// c:\MyDoc\FLOPPI\ANOTHER\avr\BLDC\int.c
//   1 #include <iom16.h>

       ASEGN ABSOLUTE:DATA:NOROOT,05cH;начинает абсолютный сегмент данных ABSOLUTE, адрес начала 0x5C
// union <unnamed> volatile __io _A_OCR0
_A_OCR0:
       DS 1;здесь определяется регистр OCR0 (зачем таким образом, непонятно).
//   2 #include <intrinsics.h>
//   3 #include "settings.h"
//   4
;далее идет описание используемых переменных и выделение для них памяти
       RSEG TINY_Z:DATA:NOROOT(0)
       REQUIRE `?<Segment init: TINY_Z>`
//   5 uint fi1;
fi1:
       DS 2

       RSEG TINY_Z:DATA:NOROOT(0)
       REQUIRE `?<Segment init: TINY_Z>`
//   6 uchar idx;
idx:
       DS 1

       RSEG TINY_Z:DATA:NOROOT(0)
       REQUIRE `?<Segment init: TINY_Z>`
//   7 uchar ampl1;
ampl1:
       DS 1

       RSEG TINY_Z:DATA:NOROOT(0)
       REQUIRE `?<Segment init: TINY_Z>`
//   8 uint freq1;
freq1:
       DS 2
//   9
//   10 extern const char SINUS [64];
//   11
//   12 #pragma vector=TIMER0_OVF_vect

       RSEG CODE:CODE:NOROOT(1)                   ;начало сегмента кода, выравнивание (1) означает,
                                                   ; что адрес сегмента выравнивается по четным адресам
                                                   ; (2 в степени 1). Такое выравнивание нужно потому, что
                                                   ; каждая команда кода программы занимает 2 байта.
//   13 __interrupt void TIMER0_OVF_routineTIMER0_OVF_routine(void)
TIMER0_OVF_routineTIMER0_OVF_routine:
//   14 {
       ST-Y, R31          ;Запись в стек данных (CSTACK) переменных. Для этого используется индексный
       ST-Y, R30          ; регистр Y (R28 Low byte, R29 High byte)
       ST-Y, R20          ;Хорошо бы от этого избавиться...
       ST-Y, R19          ;
       ST-Y, R18          ;
       ST-Y, R17          ;
       ST-Y, R16          ;
       INR20, 0x3F        ;сохраняем регистр статуса SREG (0x3F)
//   15    OCR0 = ampl1;
       LDSR16, ampl1      ;тут все понятно - выводим в регистр OCR0 переменную ampl1
       OUT0x3C, R16       ;
//   16    if (idx < 64)
       LDSR16, idx                                    ;тут тоже все понятно
       CPIR16, 64                                     ;
       BRCC??TIMER0_OVF_routineTIMER0_OVF_routine_0   ;
//   17      ampl1 = SINUS[idx];
       LDSR16, idx
       MOVR30, R16
       SUBIR30, (-(SINUS) & 0xFF)       ;вычитаем константу из регистра R30 (Z-register Low Byte)
       LDIR31, 0                        ;загрузить в R31 константу 0 (Z-register High Byte)
       LDR16, Z                         ;загрузить R16 непосредственно (по адресу в Z)
       STSampl1, R16                    ;записать в ячейку ОЗУ ampl1 регистр R16
       RJMP??TIMER0_OVF_routineTIMER0_OVF_routine_1
//   18    else if (idx < 128)
??TIMER0_OVF_routineTIMER0_OVF_routine_0:
       LDSR16, idx
       CPIR16, 128
       BRCC??TIMER0_OVF_routineTIMER0_OVF_routine_2
//   19      ampl1 = SINUS[127-idx];
       LDSR16, idx
       NEGR16
       MOVR30, R16
       SUBIR30, LOW((-(SINUS + 127) & 0xFF))
       LDIR31, 0
       LDR16, Z
       STSampl1, R16
       RJMP??TIMER0_OVF_routineTIMER0_OVF_routine_1
//   20    else if (idx < 192)
??TIMER0_OVF_routineTIMER0_OVF_routine_2:
       LDSR16, idx
       CPIR16, 192
       BRCC??TIMER0_OVF_routineTIMER0_OVF_routine_3
//   21      ampl1 = 255 - SINUS[idx-128];
       LDIR16, 255
       LDSR17, idx
       MOVR30, R17
       SUBIR30, LOW((-(SINUS - 128) & 0xFF))
       LDIR31, 0
       LDR17, Z
       SUBR16, R17
       STSampl1, R16
       RJMP??TIMER0_OVF_routineTIMER0_OVF_routine_1
//   22    else
//   23      ampl1 = 255 - SINUS[255-idx];
??TIMER0_OVF_routineTIMER0_OVF_routine_3:
       LDIR16, 255
       LDSR17, idx
       NEGR17
       MOVR30, R17
       SUBIR30, LOW((-(SINUS - 1) & 0xFF))
       LDIR31, 0
       LDR17, Z
       SUBR16, R17
       STSampl1, R16
//   24    idx = fi1 >> 8;
??TIMER0_OVF_routineTIMER0_OVF_routine_1:
       LDIR30, fi1                                ;загрузка адреса fi1 в регистр Z
       LDIR31, 0                                  ;
       LDDR17, Z+1                                ;загрузить R17 ячейкой Z+1
       MOVR16, R17                                ;R16=R17 типа делает сдвиг
       STSidx, R16                                ;записать в ячейку idx регистр R16
//   25    fi1 += freq1;
       LDIR30, freq1                              ;загрузка адреса freq1 в регистр Z
       LDIR31, 0                                  ;
       LDR16, Z                                   ;загрузка переменной freq1 в R17:R16
       LDDR17, Z+1                                ;
       LDIR30, fi1                                ;загрузка адреса fi1 в регистр Z
       LDIR31, 0                                  ;
       LDR18, Z                                   ;загрузка переменной fi1 в R19:R18
       LDDR19, Z+1                                ;
       ADDR18, R16                                ;R19:R18 = R19:R18 + R17:R16
       ADCR19, R17                                ;
       STZ, R18                                   ;Запись R19:R18 в переменную fi1
       STDZ+1, R19
//   26 }
       OUT0x3F, R20                               ;восстанавливаем регистр статуса SREG (0x3F)
       LDR16, Y+                                  ;восстановим регистры, использовавшиеся для промежуточных
       LDR17, Y+                                  ; вычислений
       LDR18, Y+                                  ;
       LDR19, Y+                                  ;
       LDR20, Y+                                  ;восстановим R20 (использовался как хранилище SREG)
       LDR30, Y+                                  ;восстановим регистр Z
       LDR31, Y+                                  ;
       RETI

       ASEGN ABSOLUTE:DATA:NOROOT,01cH            ;далее зачем-то идет описание регистров EEPROM
__?EECR:                                           ; (зачем, непонятно -все равно они не используются)

       ASEGN ABSOLUTE:DATA:NOROOT,01dH
__?EEDR:

       ASEGN ABSOLUTE:DATA:NOROOT,01eH
__?EEARL:

       ASEGN ABSOLUTE:DATA:NOROOT,01fH
__?EEARH:

       COMMON INTVEC:CODE:ROOT(1)               ;общий (COMMON) сегмент кода, находящийся
                                                 ; гарантированно, выравнивание по четным байтам (1)
       ORG 36                                   ; за векторами прерываний, с 36 байта
`??TIMER0_OVF_routineTIMER0_OVF_routine??INTVEC `:
       JMPTIMER0_OVF_routineTIMER0_OVF_routine

       RSEG INITTAB:CODE:NOROOT(0)
`?<Segment init: TINY_Z>`:
       DWSFE(TINY_Z) - SFB(TINY_Z)
       DWSFB(TINY_Z)
       DW0
       REQUIRE ?need_segment_init

       END

7. Теперь осталось заменить файл int.c на полученный файл int.s90, что совсем просто. Для этого копируем файл Debug\List\int.s90 в корневую папку проекта (там, где у нас int.c) - имеется в виду не дерево каталогов IDE (Workspace), а именно файловая папка. Затем из дерева каталогов IDE (теперь имеется в виду уже не файловая система, а именно IDE) удаляется int.c (правая кнопка\Remove) и добавляется int.s90 (правая кнопка на корневой папке проекта в браузере IDE\Add\Add Files..., тип файлов выбираем Assembler Files (*.s*;*.msa;*.asm), затем выбираем файл int.s90 и жмем Open).

8. Есть смысл также упомянуть на так называемые inline-операторы, которые позволяют вставлять ассемблерные инструкции сразу в код C, например:
asm ("sei");     //разрешить прерывания
   или управление ножкой порта в обработчиках прерывания:
#pragma vector=TIM1_OVF_vect
__interrupt void TIMER1_OVF_routine(void)
{
   asm("CBI 0x18, 2");    //asm("CBI PORTB, SERVO"), сбросить SERVO в 0
}

#pragma vector=TIM0_COMPA_vect
__interrupt void TIMER1_COMPA_routine(void)
{
   asm("SBI 0x18, 2");    //asm("SBI PORTB, SERVO"), установить SERVO в 1
}

Последнее обновление ( 30.08.2008 )
 

Комментарии  

  1. #2 ASZ
    2009-02-1304:26:49 Страница 98 IAR C/C++ Compiler Reference Guide.
    microsin: это для тех, кто хорошо читает по-английски.
  2. #1 ASZ
    2009-02-1211:40:08 Все пучком.
    Следует заметить, что для С++ нужны дополнительные пуговички :)
    microsin: очень заинтересовали - что за такие "пуговички"?..

Добавить комментарий

:D:lol::-);-)8):-|:-*:oops::sad::cry::o:-?:-x:eek::zzz:P:roll::sigh:

Защитный код
Обновить

< Пред.   След. >

Top of Page
 
microsin © 2017