Notice: Memcache::get(): Server localhost (tcp 11211) failed with: Connection refused (111) in /home/a146/www/we.easyelectronics.ru/engine/lib/external/DklabCache/Zend/Cache/Backend/Memcached.php on line 134
Портируем scmRTOS на MCU 8051 / OS и RTOS / Сообщество EasyElectronics.ru

Портируем scmRTOS на MCU 8051

Только хардкор
В конце концов меня задолбала OSAL. Основной проблемой стала не ее кривость а сам принцип кооперативных систем. По факту кооперативка это более развитая форма конечного автомата. А это означает, что при разрастании системы увеличивается количество состояний. Причем эта зависимость ни хрена не линейная. В конце концов, в большом проекте, вы получите дохренилион глобальных состояний, локальных состояний, еще более локальны состояний и еще… При этом названия у этих состояний будут самые безумные, в определенный момент вы просто задолбаетесь выдумывать новые имена.

Было решено использовать ртос. Погуглив я ничего не нашел (кроме FreeRTOS, uC, и т.п. систем). По непонятным причинам я не стал их использовать, уже не помню почему я от них отказался. В итоге захотелось портировать уже обкатанную систему scmRTOS.

Начало

Говорю сразу это оказалось намного легче чем я думал. Портировал всего за два дня. Намного сложней было отловить ошибки, на это я потратил полторы недели. Имея опыт в портировании я тоже самое могу проделать уже за вечер.

  • Я буду использовать SoC CC2510F32 и CC2530F256. У них ядро 8051 но разные модели адресации flash памяти. Это Near(прошивка менее 32Кбайт) и Banked(прошивка более 32Кбайт). Есть еще Extended 1 и Extended 2, у меня камней с подобной адресацией нет но это тоже самое только регистр DPTR 24х битный.
  • Среда разработки IAR 8051 8.10.4, в этом пункте особого выбора нет. Наличие хардварного отладчика обязательно.
  • Документация datasheet и «8051 IAR C/C++ Compiler. Reference Guide for the MCS-51 Microcontroller Family». Дока на компилятор очень важна в нее будем смотреть постоянно.


MCS-51

Это одно из самых древнейших ядер. Оно поражает своей живучестью на рынке.

Ядро очень простое. Его очень легко освоить. Все мнемоники элементарные и их можно вызубрить за полчаса. В свое время я набросал проект на 1700 строк ассемблерного кода при этом толком не зная ядра.

Важные для нас регистры: A,B,R0..R7,PSW, SP,DPTR. Вот эти регистры мы и будем сохранять/восстанавливать при переключении контекста.

Но туже сразу замечаем, что чрезмерная простота и гробит 8051. Если в AVR стек паскудный, то в 8051 это просто тихий ужас! Нам дано всего 256 байт, при этом часть стека занимает банк регистров. Также он растет в неправильную сторону.

Делаем сразу заметку. В огромных проектах RTOS использовать невозможно. Банально не хватит стека.

IAR

Изучение «8051 IAR C/C++ Compiler. Reference Guide for the MCS-51 Microcontroller Family» в обязательном порядке!

Из неe мы узнаем, что есть еще виртуальные регистры V0..V31. Количество их задается в настройках проекта. Они используются как локальные буферы (связанно с особенностями ядра MCS-51, к некоторым байтам мы можем обращаться прямо а не косвенно).
Также есть регистр VB, адрес которого указывается в файле конфигурации линковщика.
Узнаем еще, что IAR отлично знает о проблемном стеке и предоставляет нам на выбор два программных стека PSTACK(размер 256 байт) и XSTACK( размер до 64Кбайт). В этих стеках сохраняется локальный контекст при входе в функцию/прерывание.

Параметры в функцию передаются через регистры

Ну а если не влазит, то через программный стек.

Итого: по мимо основных регистров нам нужно сохранять еще и регистры IAR (VB,V0..V31,XSP).

Контекст

Как уже стало ясно контекст будет состоять из регистров 8051 и виртуальных регистров IAR. А еще забегая на перед скажу, что нужен еще флаг EA(глобальный флаг разрешения прерываний).

Я буду использовать только один банк регистров, т.к. использование всех 4х банков не дает преимуществ.

Контекст:
A — 1 байт
B — 1 байт
PSW — 1 байт
R0..R7 — 8 байт. Располагаются в начале стека по адресу 0x00-0x07
SP — 1 байт.
DPTR — 2 байта.
V0..V31 — от 8 до 32 байт. Располагаются сразу за банком регистров.
VB — 1 байт. Имеет фиксированный адрес 0x20. Адрес можно изменить в настройках линковщика.
SPX — 2 байта. Располагается сразу за виртуальными регистрами.
EA — 1 байт.

Итого размер контекста от 24 до 50 байт.


Кусок стека.
Желтый — регистры.
Красный — виртуальные регистры.
Голубой — SPX
Зеленый — VB




Начинаем портировать.

С теорией разобрались. Пора приступать к практике.
Наиболее подходящим по духу является порт для AVR. В нем тоже используется раздельный стек. Вот его мы и возьмем за основу.

Сначала создадим проект пустышку и добавим к нему все нужные файлы.


Я думаю вы уже удосужились прочитать доку по scmRTOS.

Настраиваем





Выбираем максимально возможный(для этой конфигурации) размер ISTACK.
Забегая наперед скажу. Адрес расположения стека строго фиксированный. Мы можем менять только указатель на голову и только в пределах одной страницы. Естественно адреса возвратов должны хранится только в хардварном стеке. Также компилятор IAR подкидывает пакость. Мы не можем разместить свой массив в секции ISTACK во время компиляции. Можно только зарезервировать место вот так

__idata uint8_t r_stack0[32];


Эта строка не может находиться внутри класса.

Такой метод резервирования стека возвратов для процесса не подходит. Т.к. придется менять код ядра.

Поэтому я пошел другим путем. Создаю большую секцию ISTACK и программно формирую указатели(это будет впереди). При использовании такого способа почти не нужно изменять ядро.

XDATA — это наш XSTACK. Под него не требуется много места. Болеет того он используется только на старте. Если вам остро не хватает оперативки, то вы запросто можете определить смещение на секцию XSTACK(я думаю вы удосужились прочитать доку на компилятор) и смело использовать это место в процессах.

Добавляем пути для асма и C++.
$PROJ_DIR$\Src
$PROJ_DIR$\..\scmRTOS\8051
$PROJ_DIR$\..\scmRTOS\Common
$PROJ_DIR$\..\scmRTOS\Extensions\Profiler
$PROJ_DIR$\..\ccxx10


Пустышка готова. Хотя я еще рекомендую по удалять все лишнее из примера. Так будет проще работать.

Main

//---------------------------------------------------------------------------
int main()
{
    // Disable global interrupt by setting the [IEN0.EA=1]
    EA = 0;
   
    // Change clock source for low power oscillator to LS RCOSC.
    CLKCON |= CLKCON_OSC32;
    // Power up unused oscillator (HS XOSC).
    SLEEP &= ~SLEEP_OSC_PD;
    // Wait until the HS XOSC is stable.
    while( !(SLEEP & SLEEP_XOSC_S) );
    // Change the system clock source to HS XOSC.
    CLKCON &= ~CLKCON_OSC;
    // Wait until source has actually changed.
    while (CLKCON & CLKCON_OSC);
    // Power down the HS RCOSC, since it is not beeing used.
    // Note that the HS RCOSC should not be powered down before the applied
    // system clock source is stable (SLEEP.XOSC_STB = 1).
    SLEEP |= SLEEP_OSC_PD;
              
    //System timer
    T4CTL = T4CTL_DIV_128  | T3CTL_START | T4CTL_OVFIM | T4CTL_CLR | T4CTL_MODE_FREERUN; // 2.52ms
    T4IE = 1;
  
    //
    OS::run();
}


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

OS_Target.h

#if (((__TID__ >> 8) & 0x7F) != 0x20)
#error "This file must be compiled for 8051 processor only."
#endif


Только для 8051.

//------------------------------------------------------------------------------
//
//      IAR Systems include file[s]
//
#include <intrinsics.h> // interrupt state handling


Добавляем макросы для работы с прерываниями.

#define INLINE _Pragma("inline=forced") inline


Об этом дефайне немного по подробней. Это указание компилятору делать функцию с этим дефайном всегда инлайновой. Но по каким-то причинам IAR любит активно ее игнорить. Даже когда вы выставите наиболее благоприятные для нее настройки оптимизации, то иногда IAR все равно будет ее игнорить. Возможно это связанно с особенностями архитектуры. Поэтому на нее не надеемся.

#define SYS_TIMER_CRIT_SECT()  TCritSect cs
#define CONTEXT_SWITCH_HOOK_CRIT_SECT() TCritSect cs

#define SEPARATE_RETURN_STACK  1


В MCS-51 прерывания могут вытесняться другими прерываниями. Поэтому важные моменты должны быть в критической секции. А также говорим системе, что стек используется раздельный.

class TCritSect
{
public:
    TCritSect () : StatusReg(__get_interrupt_state()) { __disable_interrupt(); }
    ~TCritSect() { __set_interrupt_state(StatusReg); }

private:
    status_reg_t StatusReg;

};


Код крит. секции. Тут и так все понятно.

#define get_return_sp() SP
#define set_return_sp(x) {SP=x;}

#pragma segment="ISTACK"
#define set_isr_stack_pointers() {SP = reinterpret_cast<uint16_t>(__segment_begin("ISTACK")) + 1;}


Добавляем скрипты для работы с указателем стека.

struct TSavedSP
    {
        stack_item_t ReturnSP;
    };


Правим структуру которая используется в обвертке TISRW_SS

class TISRW
    {
    public:
        INLINE  TISRW()  { ISR_Enter(); }
        INLINE  ~TISRW() { ISR_Exit();  }

    private:
        //-----------------------------------------------------
        INLINE void ISR_Enter() // volatile
        {
            Kernel.ISR_NestCount++;
            enable_interrupts();
        }
        //-----------------------------------------------------
        INLINE void ISR_Exit()
        {
            TCritSect cs;   //disable_interrupts();
            if(--Kernel.ISR_NestCount) 
            {
              return;
            }
            Kernel.sched_isr();
        }
        //-----------------------------------------------------
    };
   


В обвертке TISRW немного меняется только деструктор. В AVR на выходе из прерывания устанавливается флаг EA аппаратно. В 8051 его нужно выставлять руками, да и он может быть вовсе запрещен. Поэтому используем крит секцию.

class TISRW_SS
    {
    public:
      INLINE  TISRW_SS()  
      {
          if(Kernel.ISR_NestCount++ == 0)
          {
             SavedSP.ReturnSP = get_return_sp();
             //SavedSP.DataSP = get_data_sp();
             
             set_isr_stack_pointers();
          }
              
          enable_interrupts();
      } 
      
      
      INLINE  ~TISRW_SS()
      {
            TCritSect cs;
            
            if(--Kernel.ISR_NestCount) return;
            
            set_return_sp(SavedSP.ReturnSP);
            //set_data_sp(SavedSP.DataSP);
            
            Kernel.sched_isr();
      }

    private:
    };


А тут уже идут сильные изменения. Это связанно с игнорирование INLINE. Деструктор/конструктор переписан так, чтоб ее не проигнорировать.

Также в порте AVR происходит переключение софтварного стека. В этом случае этого делать нельзя. IAR подбросил еще одну свинку. Даже если функция инлайновая и IAR это принял, то он все рано перед входом в функцию сохраняет локальный контекст в XSTACK.

Получается следующее
Сохранение локального контекста -> переключаем софтварный стек -> восстанавливаем мусор из стека прерывания на выходе из конструктора.

Поэтому переключается только хардварный стек.

Создание процесса


Тут скрепя зубами придется изменить немного ядро. Это связанно с тем как выделяется место под стек возврата.

typedef OS::process<OS::pr0, 220,39> TProc1;

220 — локальный стек. С этим проблем нет, это обычный массив.
39 — стек возврата. Это указание сколько нужно отрезать от ISTACK.

Для начала топаем к шаблону в OS_Kernel.h

template<TPriority pr, size_t stack_size, size_t rstack_size>
		class process : public TBaseProcess
		{
		public:
                        INLINE_PROCESS_CTOR process();

			OS_PROCESS static void exec();

		#if scmRTOS_PROCESS_RESTART_ENABLE == 1
			INLINE void terminate();
		#endif

		private:
			stack_item_t Stack [stack_size/sizeof(stack_item_t)];
			//stack_item_t RStack[rstack_size/sizeof(stack_item_t)];
                        stack_item_t *RStack;
		};


		template<TPriority pr, size_t stack_size, size_t rstack_size>
		process<pr, stack_size, rstack_size>::process() : TBaseProcess( &Stack[stack_size/sizeof(stack_item_t)]
		                                                   , RStack
                                                                   , rstack_size
                                                                   , pr
                                                                   , reinterpret_cast<void (*)()>(exec)
                                                           #if scmRTOS_DEBUG_ENABLE == 1
                                                                        , Stack
                                                                        , RStack
                                                           #endif
                                                                        )
		{
		}


RStack у нас теперь не массив а указатель и он передается конструктору(по фату нужен адрес на указатель, но изменения ядра должны быть минимальны).

Кстати обратите внимание как передается указатель на софтварный стек процесса &Stack[stack_size/sizeof(stack_item_t). Т.е. передает указатель на последний элемент массива. Ведь он у нас растет в правильную сторону.

В конструкторе TBaseProcess тоже вносим изменения.
TBaseProcess::TBaseProcess( stack_item_t * Stack
                          , stack_item_t * RStack
                          , stack_item_t  rs_size 
                          , TPriority pr
                          , void (*exec)()
                      #if scmRTOS_DEBUG_ENABLE == 1
                          , stack_item_t * aStackPool
                          , stack_item_t * aRStackPool
                      #endif
                          ) : StackPointer(Stack)
                            , Timeout(0)
                            , Priority(pr)
                      #if scmRTOS_DEBUG_ENABLE == 1
                            , WaitingFor(0)
                            , StackPool(aStackPool)
                            , RStackPool(aRStackPool)
                      #endif 
                      #if scmRTOS_PROCESS_RESTART_ENABLE == 1
                            , WaitingProcessMap(0)
                      #endif

{
    TKernel::register_process(this);

    init_stack_frame( Stack
                    , RStack
                    , rs_size
                    , pr
                    , exec
                #if scmRTOS_DEBUG_ENABLE == 1     
                    , aStackPool
                    , aRStackPool
                #endif  
                    );
}


Мы добавляем переменную rs_size(размер стека возврата) и pr(приоритет, он же номер в массиве).

Метод init_stack_frame должен знать размер стека возврата и того кто передает(этот параметр нужен только для сброса или функций отладки). Опят же, это вынужденные изменения.

OS_Target_cpp.cpp

//------------------------------------------------------------------------------
#pragma vector=T4_VECTOR
OS_INTERRUPT void OS_SystemTimer_ISR(void)
{ 
    /* Clears the CPU interrupt flag. */
    T4IF = 0;  
    /* Clears the module interrupt flag. */
    T4OVFIF = 0;   
    
    scmRTOS_ISRW_TYPE ISR;  
    
    Kernel.system_timer();
    
   
    #if scmRTOS_SYSTIMER_NEST_INTS_ENABLE == 1
        //ABLE_NESTED_INTERRUPTS();
    #else 
        TCritSect cs;
    #endif

    #if scmRTOS_SYSTIMER_HOOK_ENABLE == 1
    system_timer_user_hook();
    #endif   
}


Тут у нас обработчик системного таймера. Изменений как таковых нет, кроме scmRTOS_SYSTIMER_NEST_INTS_ENABLE. Помним, что в 8051 есть вытеснения прерываний. Поэтому если scmRTOS_SYSTIMER_NEST_INTS_ENABLE=1, то мы ничего не делаем ибо уже все и так разрешено. А если =0, то ставим крит секцию.

Инициализация контекста

void TBaseProcess::init_stack_frame( stack_item_t * Stack
                                   , stack_item_t * RStack
                                   , stack_item_t  rs_size 
                                   , uint8_t pr  
                                   , void (*exec)() 
                                #if scmRTOS_DEBUG_ENABLE == 1
                                   , stack_item_t * StackPool
                                   , stack_item_t * RStackPool
                                #endif
                                   )
{ 
     static uint8_t free_space=0xFF; //0x21-0xFF
     
     free_space-=rs_size;
          
     if(free_space<=0x21)
     {
       while(1); //"rstack overflow"
     }
     
      RStack = ( stack_item_t*)( HARDWARE_STACK_ADDRESS + free_space);  //stack point 
     
     #if scmRTOS_DEBUG_ENABLE == 1
      RStackPool=RStack+rs_size;
      
      this->RStackPool=RStackPool; 
     #endif 
     
      #if scmRTOS_PROCESS_RESTART_ENABLE == 1
         RStack_point[pr]=RStack;
         
         #if scmRTOS_DEBUG_ENABLE == 1
          RStack_pool_point[pr]=RStackPool;
         #endif 
      #endif          
         
         
      *(++RStack)=reinterpret_cast<uint16_t>(exec);
      *(++RStack)=reinterpret_cast<uint16_t>(exec)>>8;
    
    StackPointer = Stack;      

    *(--StackPointer) =0x80;    //IEN0 register
    StackPointer     -= 1+2;             //A+DPTR
    *(--StackPointer) = reinterpret_cast<uint16_t>(RStack);// RStack;          //SP  
     StackPointer-=1+8+VREG_COUNT+1+1;   //PSW+REG+VREG_COUNT+VB+B
     
     uint8_t _spxl=(uint16_t)(StackPointer-2);
     uint8_t _spxh=((uint16_t)(StackPointer-2)>>8);
     
    *(--StackPointer) = _spxl;   //SPX
    *(--StackPointer) = _spxh;
   
    

#if scmRTOS_DEBUG_ENABLE == 1
    //-----------------------------------------------------------------------
    //   Fill stack pools with predefined value for stack consumption checking
    //   Do not fill area for GPR and SFR on data stack
    stack_item_t *fill_ptr;
    fill_ptr = RStack;
    while( fill_ptr < RStackPool )
    {
	*++fill_ptr = STACK_DEFAULT_PATTERN;
    }
    fill_ptr = StackPointer;
    while( fill_ptr > StackPool )
    {
	*--fill_ptr = STACK_DEFAULT_PATTERN;
    }
#endif // scmRTOS_DEBUG_ENABLE
}


Один из самых важных методов. Он один для всех процессов. Поэтому мы можем использовать статичную переменную для контроля свободного места.

free_space=0xFF; По умолчанию выставлена на конец стека.

free_space-=rs_size;
          
     if(free_space<=0x21)
     {
       while(1); //"rstack overflow"
     }


Отрезаем кусочек и проверяем на переполнение. Если переполнение есть, то вешаемся. Лучше не работать вообще, чем работать неправильно. Ох если бы вы знали как меня заипали плавающие ошибки!!! Это ад их отлавливать! Одну из ошибок я отлавливал 8 часов подряд.

RStack = ( stack_item_t*)( HARDWARE_STACK_ADDRESS + free_space); //stack point 


Формируем указатель.
К дефайну HARDWARE_STACK_ADDRESS мы еще вернемся.

*(++RStack)=reinterpret_cast<uint16_t>(exec);
      *(++RStack)=reinterpret_cast<uint16_t>(exec)>>8;


Записываем в istack стек адрес возрата.

*(--StackPointer) =0x80;    //IEN0 register
    StackPointer     -= 1+2;             //A+DPTR
    *(--StackPointer) = reinterpret_cast<uint16_t>(RStack);// RStack;          //SP  
     StackPointer-=1+8+VREG_COUNT+1+1;   //PSW+REG+VREG_COUNT+VB+B
     
     uint8_t _spxl=(uint16_t)(StackPointer-2);
     uint8_t _spxh=((uint16_t)(StackPointer-2)>>8);
     
    *(--StackPointer) = _spxl;   //SPX
    *(--StackPointer) = _spxh;


А это наш контекст на момент старта процесса. IAR за нас уже обнулил все массивы. Так, что нули будут гарантированно.

В начале записываем бит EA. После записываем, только младшую часть стека возврата. А под конец ставим указатель SPX на конец контекста.

Это выглядит вот так


Софтварный стек растет вниз поэтому он не портит контекст.

А, что если внутри процесса есть переменные? Произойдет следующее:
1) При входе в процесс XSP будет указывать на свободное место.
2) IAR затолкает все в софтварный стек. Т.е. в стек процесса.
3) При переключение контекста он обновиться и будет указывать на новое место.



scmRTOS_CONFIG.h


#define  BANK_OFFSET 0
#define  REG_COUNT   8  //register counter (always 8 )

#define  VREG_COUNT  8 //virtual register counter

#define HARDWARE_STACK_ADDRESS 0xFF00 //0x1F00   //see in the datasheet

#define  VB 0x20   //see linker config (lnk51ew_cc2510F32.xcl)


По факту здесь должны быть только настройки scmRTOS. Но я немного нарушил правила и добавил еще и настройки проекта.

HARDWARE_STACK_ADDRESS это значение мы берем из даташита на конкретный микроконтроллер. Оно указывает по какому адресу находиться стек.

OS_Target_asm.s51

Это казалось бы самое сложное, но это самое простое. Ведь асм на 8051 чрезвычайно прост.

Нужно портировать сразу два способа переключения контекста.
1) Прямая передача. Это когда мы говорим прямо, что нужно переключить контекст.
2) Использование программного прерывания. Это когда мы ОС просим переключить контекст а она уже его переключит когда будет наиболее удобно для этого.

Про прямую передачу говорить особо нечего. Там все просто. На входе получили два адреса. В один записали, из другого восстановили. И все.
Для сохранения контекста я использовал 6ть буферов.
RSEG IDATA_I
    
     //space for local buffer
    RR0: DS 1
    RR1: DS 1
    RR2: DS 1
    RR3: DS 1
    RR4: DS 1
    RR5: DS 1


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

С программным прерывание тоже оказалось все просто

common INTVEC
    org CONTEXT_SWITCH_ISR_VECTOR
    ljmp ContextSwitcher_ISR
    
    RSEG CODE_C
ContextSwitcher_ISR:


Регистрируем свой обработчик.

На входе запрашиваем адрес текущего контекста

lcall os_context_current_get


И на выходе адрес нового контекста

lcall os_context_switch_hook


И на этом все. Стоит только заметить, что флаг EA должен восстанавливаться последним. Ведь прерывание может быть вытесненным.

А какое прерывание нужно использовать? Самое ненужное и самое не приоритетное.
Я использовал прерывание WDT_Timer это одно из самых ненужных прерываний да и на работу WDT оно никак не влияет.

Описывается оно в scmRTOS_TARGET_CFG.h

#define CONTEXT_SWITCH_ISR_VECTOR  WDT_VECTOR


А это для его активации
INLINE void raise_context_switch() {IRCON2|=(1<<4); SPM_CONTROL_REG |= IEN2_WDTIE;    } // enable SPM interrupt
INLINE void block_context_switch() { IRCON2&=~(1<<4); SPM_CONTROL_REG &= ~IEN2_WDTIE;  } // disable SPM interrupt


Низший приоритет у него стоит по дефолту. И во время работы нельзя повышать его приоритет.

Почти конец

На форуме электроникса _Артём_ мне подсказал, что с вытесняющими прерываниями не все так просто.
Ведь когда мы попадаем в прерывание, то в 8051 глобально они все еще разрешены. Их нужно запретить до попадания в конструктор обвертки. Иначе это может привести к лишнему переключению контекста.

Сказано сделано. Первой командой так и сделал. Глянул, что сгенерил IAR и вижу, что запрет прерываний ни разу не первая команда. Сначала IAR в лучших своих традициях сохраняет локальный контекст а уже потом запрещает прерывания. Немного документации и выход нашелся. Дело в том, что в 8051 под вектор прерывания выделяется 8байт памяти. Вот туда мы и можем вставить свой мини обработчик.

Делается это легко.

Сначала делаем редефайн векторов на новый адрес.

#undef  RFTXRX_VECTOR
#undef  ADC_VECTOR
#undef  URX0_VECTOR
.........

#define  RFTXRX_VECTOR  VECT(  0, 0x03+2 )   /*  RF TX done / RX ready                       */
#define  ADC_VECTOR     VECT(  1, 0x0B+2 )   /*  ADC End of Conversion                       */
#define  URX0_VECTOR    VECT(  2, 0x13+2 )   /*  USART0 RX Complete                          */
......


2 — это размер инструкции сброса флага прерываний.

Говорю сразу. Адреса векторов прерываний у всех 8051 стандартны. Меняются только названия. Поэтому в другом камне максимум нужно изменить имя вектора.

А дальше в ассемблерном файле пишем свой обработчик

COMMON INTVEC
   
    ORG 0x03//RFTXRX_VECTOR-2
    CLR   IEN0.7
    
    ORG 0x0B//ADC_VECTOR-2
    CLR   IEN0.7
    
    ORG 0x13//URX0_VECTOR-2
    CLR   IEN0.7




Вуаля. Первая инструкция это гарантированное запрещение прерывания. Ее вытеснить невозможно.

Конец

Как видим ничего сложно нет. Все элементарно. Забавно но на фрилансе подобная услуга стоит 1000 у.е. Хотя по факту работы всего на 300-400у.е.

Это не пособие HOWTO а пример как такое можно сделать уже с другим микроконтроллером.
Исходники можно взять тут. Естественно мелочь была опущена.

Думаю скоро они появятся в репозитории.

Тут еще нужно вставить фотку котят и ссыль на ютуб. Где будет показан пример мегающих светиков и я 2-3 часа рассказываю о том как это работает. Но это за отдельную плату.

PS: Вспомнил почему отверг все другие ОС. Дело в компиляторе и отладчике. IAR хорошо дружит с продукцией Ti.
  • +11
  • 20 ноября 2012, 03:24
  • a9d

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

RSS свернуть / развернуть
Тут еще нужно вставить фотку котят и ссыль на ютуб. Где будет показан пример мегающих светиков и я 2-3 часа рассказываю
бубню на грани слышимости и через два слова на третье начинаю мычать и замолкать
о том как это работает. Но это за отдельную плату.

+100500! :)
0
Не надо так явно демонстрировать своё непонимание содержательной части, опыт придёт со временем. В отличие о текста, видео сделать очень не просто, ибо оно покажет весь уровень владения инструментарием в комплексе и что на словах просто в тексте на практике объяснить и донести не каждый сможет. Это не глупые однообразные комменты травить по постам.

А по статье интересно замечание о паскудности стека в AVR. Это в каком смысле? Что в архитектуре не так?
+1
Над указателем стека можно проводить только операции типа «mov».
0
А в чём выражается ограничения при работе с указателем? Я почему обратил на это внимание, т. к. имею опыт по компиляции одного проекта для AVR на C++ в двух различных компиляторах. У IAR и GCC по-разному осуществляется работа со стеком, но с использованием одной архитектуры, поэтому я не пойму теперь достоинство это или недостаток?
Работа со стеком в gcc мне нравится больше, ибо мне не нужно думать о размере стека или о его срыве при использовании локальных переменных, в IAR'е же я должен прикидывать размеры локальных переменных, чтобы не случился срыв, поэтому то, что работает в gcc далеко не всегда при идентичном виде заработает в iar.
Это одна из тех вещей, которые мне в iar не нравятся, так как требуют углубленного понимания работы компилятора изнутри, но сам iar мне нравится, потому я стараюсь его не забывать.
0
Доступна только команда mov. Никакой косвенной адресации. Если локальных переменных дохрена(на них не хватает регистров), то это приводит к печальке.

В 8051 тоже нет косвенной адресации для SP. А вот для SPX есть. Ибо эта пара храниться в первой половине стека.
+1
точнее для DPTR оступна косвенная адресация.
0
Давно не заглядывал в свои ассемблерные листинги, забыл уже как конкретно отличаются подходы gcc и iar, но чисто теоретически, что мешает, к примеру, сохранять указатель стека в одной из регистровых пар, для которых доступна косвенная адресация? Этого не делают, но это не означает, что это невозможно. Такой ли уж это минус тогда? Можно пихать в стек что угодно и косвенно обращаться к любому из локальных участков, это будет медленнее, чем с регистрами, видимо в этом и причина.
0
хз почему в IAR такой метод не используют. В теории это должно сработать. Возможно в таком подходе есть подводные камни.
0
IAR хорошо дружит с продукцией Ti.
Не совсем понял, каким боком связаны TI и сабж.
А так — зачёт!
0
Сорри. Ступил…
0
Ну как бы дебагер и вся продукция доступна. А для других компиляторов придется ваять конфиги линковщика и непонятно как подключать дебаггер.
0
А это означает, что при разрастании системы увеличивается количество состояний
можно пример реального кода с кучей состояний? Использую Protothreads, состояние хранится в одном int. Если нужно, добавляются глобальные переменные/структуры. В отличии от вытесняющих ОС не нужен огромный стек.
0
не можно. Коммерческая тайна. У меня более 4х десятков состояний. Это п***ц как много. И это все нужно хранить в голове и постоянно знать. В РТОС это не требуется.
0
Что за OSAL? И откуда в кооперативке состояния?
0
OSAL это детище Ti. Кооперативка работает как конечный автомат. Функция процесса завершается и запускается снова с новым состоянием.
0
Бред какой-то. У функции могут быть свой\и автомат\ы. На хера плодить геморрой?!
0
Эм. Я так полагаю ты говоришь о том чего не знаешь.
0
Честно скажу, не знаю, что такое OSAL. Не нашел русскоязычных источников. Но уже то, что я прочитал про состояния в OSAL, мне кажется бредом. Я программирую AVR на асме. Си все порываюсь изучать, да как-то все бросал на полпути. Дальше нескольких примерчиков дело так и не зашло. Для AVR написал простенькую кооперативную карусельку. Для вытесняющей задач до сих пор так и не нашлось. Кооперативной карусельки вполне хватает.
0
Хм. Используя кооперативку ее же называешь бредом и продолжаешь использовать.
0
А у меня нет там состояний. У функций свои состояния.
Не буду пустое говорить. Про какие состояния в кооперативках вы говорите? Также состояния в OSAL меня сильно заинтересовали. Что там за состояния такие, что программирование превращается в геморрой.
0
тогда у тебя не кооперативка а просто по кругу вызываются функции.
0
Я и написал. Простая каруселька. Так все-таки, что там с состояниями?
0
скачай любую кооперативку и посмотри диспетчер.
0
в AVR стек паскудный
интересно почему?
0
  • avatar
  • Nemo
  • 21 ноября 2012, 12:25
Ребята, вопрос срочный про программатор серии 8051

Нужно прошить проц AT80C32X2 (P80C32X2), нужен комплекс программатор — IDE

Что можете предложить?
0
В даташите же есть протокол прошивки, можно на первой попавшейся демоборде собрать.
IDE, если нужна для написания прошивки — Keil C51 наверняка поддерживает.
0
80C32 не имеет ПЗУ, ему нужно внешнее + регистр-защелка. Программатор типа TL866CS (A) и IAR 8051 или даже MIDE (я юзаю, мне нравится).
0
По даташиту — есть OTP ROM версии.
0
Да, 83xx или 87xx… Но не 80xx точно. Я его мучил вдоль и поперек)
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.