AVR. Учебный курс. Стартовая инициализацияPrint This Post

Автор DI HALT
Опубликовано 09 июля 2008 
Рубрики: AVR. Учебный курс
Метки: , ,

Инициализация памяти
Мало кто подозревает о том, что при включении в оперативке далеко не всегда все байты равны 0xFF. Они могут, но не обязаны. Равно как и регистры РОН не всегда равны нулю при запуске. Обычно да, все обнулено, но я несколько раз сталкивался со случаями когда после перезапуска и/или включения-выключения питания, микроконтроллер начинал творить не пойми что. Особнно часто возникает когда питание выключаешь, а потом, спустя некоторое время, пара минут, не больше, включаешь. А всему виной остаточные значения в регистрах.

Итак, возьмите себе за правило после каждого включения, в разделе инициализации, еще даже до инициализации стека, делать зануление памяти и очистку всех регистров. Разумеется делается это все в цикле. Вот примерный вариант кода:

1
2
3
4
5
6
7
8
9
10
11
12
RAM_Flush:	LDI	ZL,Low(SRAM_START)	; Адрес начала ОЗУ в индекс
		LDI	ZH,High(SRAM_START)
		CLR	R16			; Очищаем R16
Flush:		ST 	Z+,R16			; Сохраняем 0 в ячейку памяти
		CPI	ZH,High(RAMEND+1)	; Достигли конца оперативки?
		BRNE	Flush			; Нет? Крутимся дальше!
 
		CPI	ZL,Low(RAMEND+1)	; А младший байт достиг конца?
		BRNE	Flush
 
		CLR	ZL			; Очищаем индекс
		CLR	ZH

Поскольку адрес оперативки у нас двубайтный, то мы вначале смотрим, чтобы старший байт совпал с концом, а потом добиваем оставшиеся 255 байт в младшем байте адреса.
Далее убиваем все регистры от первого до последнего. Все, контроллер готов к работе.

1
2
3
4
5
		LDI	ZL, 30		; Адрес самого старшего регистра	
		CLR	ZH		; А тут у нас будет ноль
		DEC	ZL		; Уменьшая адрес
		ST	Z, ZH		; Записываем в регистр 0
		BRNE	PC-2		; Пока не перебрали все не успокоились

За процедурку зануления регистров спасибо Testicq

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

З.Ы.
Кстати, о оперативке. Нашел я недавно планку оперативной памяти на 1килобайт, древнюю как говно мамонта, еще на ферромагнитных кольцах.

Комментарии

20 комментариев на «AVR. Учебный курс. Стартовая инициализация»


  1. nwanomaly 30 Сен 2008 21:17

    я ещё помню, когда смотрел по линку на программатор на сайт Николаева, то тоже видел там раздел про “тонкости”. про использование символических имён, работу с флагами и компаратор в режиме пониженно потребления ) мелочи, а знать надо


  2. Ivan A-R 30 Сен 2008 21:23

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

    Как вариант, хранить во флеше слепок начальных значений переменных и копировать их. Или просто не забывать инициировать их по мере надобности.

    А чистить регистры и место для стека считаю пустой тратой тактов =) Регистры ты так или иначе будешь инициировать по ходу дела, а стек у тебя изначально нулевой и при росте перекроет любые левые значения.

    DI HALT

    Ну так а что мешает потом забить переменные нужынми значениями. Все проще чем слепок памяти держать.

    По поводу регистров. Да вот вчера трахался с тем, что у меня на Tiny2323 после вырубания питания значение в регистре торчало по несколько минут!!! Врубаю МК - на тебе - мигать начинать самопроизвольно. Мигание у меня зависило от состояния регистра R20.

    Ivan A-R

    Сам подход, что ты полагаешь, что у тебя в регистрах есть значения по умолчанию - порочен. Сейчас ты чистишь всё что можно после сброса и это работает. Но когда тебе потребуется в ходе работы переинициализировать какой-то модуль, ты опять столкнёшься с тем, что в регистрах у тебя мусор. Каждый модуль должен сам инициализировать то что ему нужно.
    Я у себя для каждого модуля пишу процедуру void xxxInit(void), которая полностью готовит модуль к работе. И это при том, что ‘C’ умеет инициировать переменные при старте.

    DI HALT

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

    Ivan A-R

    Знаю. Я тоже пишу на асме. Но это тебя не освобождает от инициализации переменных =)

    Тут фишка такая, когда ты используешь умолчальные инициализации, ты тереяшь в понятности кода.

    Сделай инициализацию явно, и уже потом, на этапе оптимизации сможешь эту строчку закомментить с комментарием “// а оно и так уже такое”. В дальнейшем избавишь себя от головной боли.


  3. SWG 01 Окт 2008 0:47

    Задание начальных значений переменным должно сидеть в подсознании, на чем бы не писал. В этом отношении хороши компиляторы семейства Паскалей. Например, в Дельфи компилятор постоянно предупреждает, если какая-то переменная может перед использованием иметь неопределенное значение, (например, значение задается в куске кода или цикле, который может в некоторых случаях не выполняться. Си в этом отношении не так строг, много чего пропускает, что потом сносит башку при отладке.
    Вчера я начал писать программку уже для контроллера бамперов своего робота, и в самом начале, в процедуре инициализации, сразу поставил кусок (В ходовом контроллере тоже):

    if PCON.1 = 0 then // Если был сброс по питанию, Очищаем память!
    begin
    FSR := $20; //Начало ОЗУ
    while FSR < $40 do // Первые 32 байта.
    begin
    INDF := 0; // Очищаем память!
    Inc(FSR);
    end;
    PCON.1 := 1;
    end;
    Т.Е. по холодному старту очищаю область памяти, используемую пока в программе, далее в программе тем переменным, которые должны быть отличны от нуля, присваиваются конкретные значения. Сначала хотел сделать этот кусок на асме, но потом посмотрел, как его расписал компилятор, и решил оставить на Паскале. Вот этот кусок после компиляции:

    ;ROBO_SWG_2.ppas,81 :: begin
    ;ROBO_SWG_2.ppas,82 :: if PCON.1 = 0 then // Если был сброс по питанию, Очищаем память!
    $00FA $1303 BCF STATUS, RP1
    $00FB $1683 BSF STATUS, RP0
    $00FC $080E MOVF PCON, 0
    $00FD $00F1 MOVWF STACK_1
    $00FE $3000 MOVLW 0
    $00FF $18F1 BTFSC STACK_1, 1
    $0100 $3001 MOVLW 1
    $0101 $00F1 MOVWF STACK_1
    $0102 $0871 MOVF STACK_1, 0
    $0103 $3A00 XORLW 0
    $0104 $1D03 BTFSS STATUS, Z
    $0105 $2910 GOTO ROBO_SWG_2_L_9
    $0106 $ ROBO_SWG_2_L_8:
    ;ROBO_SWG_2.ppas,84 :: FSR := $20; //Начало ОЗУ
    $0106 $3020 MOVLW 32
    $0107 $0084 MOVWF FSR
    ;ROBO_SWG_2.ppas,85 :: while FSR < $40 do // Первые 32 байта.
    $0108 $ ROBO_SWG_2_L_12:
    $0108 $3040 MOVLW 64
    $0109 $0204 SUBWF FSR, 0
    $010A $1803 BTFSC STATUS, C
    $010B $290F GOTO ROBO_SWG_2_L_13
    ;ROBO_SWG_2.ppas,87 :: INDF := 0; // Очищаем память!
    $010C $0180 CLRF INDF, 1
    ;ROBO_SWG_2.ppas,88 :: Inc(FSR);
    $010D $0A84 INCF FSR, 1
    ;ROBO_SWG_2.ppas,89 :: end;
    $010E $2908 GOTO ROBO_SWG_2_L_12
    $010F $ ROBO_SWG_2_L_13:
    ;ROBO_SWG_2.ppas,90 :: PCON.1 := 1;
    $010F $ ROBO_SWG_2_L_16:
    $010F $148E BSF PCON, 1
    $0110 $ ROBO_SWG_2_L_17:
    ;ROBO_SWG_2.ppas,91 :: end;

    Всего 23 байта. Можно сделать меньше, но зачем?
    При других видах сброса (например, по сторожевому таймеру) память пока не очищаю, (вдруг какие-то данные понадобятся), только задаю значения основным переменным.

    nwanomaly

    я больше программлю под обычные писишки. на сях в основном.
    про инициализацию в общем могу сказать так. вначале надо изучать компилятор. как и что он устанавливает при определении переменной. изучить проц - обнуляется ли в нём всё при старте?

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

    я конечно понимаю, что тут не бывает - когда в массив 1000 на 1000 забивают мемкопи инициализирующий )

    вообщем, инициализация тоже имеет в себе тонкости.

    SWG

    В микроконтроллерах при сбросе или холодном старте обычно задается наиболее безопасная конфигурация периферии в спецрегистрах, регистры же общего назначения и оперативка никогда не очищаются, по крайней мере в массовых типах микроконтроллеров. (Среди тех PIC, ATMEL и INTEL, про которые читал или использовал сам, таких не попадалось). Так что “Спасение утопающих - дело рук самих утопающих”. Просто должно войти в привычку не пускать ничего на самотек, все определять заранее. Сразу отпадет куча проблем, а высвободившееся при отладке время можно будет направить на повышение “интелекта” самой программы.


  4. Cluster 01 Окт 2008 11:19

    А разьве прерывание таймера не выполнится после выхода из прерывания INT0?

    DI HALT

    Выполнится, но мне надо было прерывание таймера внутри обработчика INT0

    Cluster

    Слабо представляю зачем это нужно. Сколько по времени у тебя выполнялся обработчик INT0?

    DI HALT

    На обработчик INT0 у меня повешен альтернативный режим работы девайса. Так что он перехватывает на себя управление и держит его до тех пор пока не отработает всю логику.

    SWG

    Вот тут как раз бы и пригодились многоуровневые приоритетные прерывания. Хорошая была штука! При работе в реальном времени с информацией, которую специально для контроллера никто повторять не будет, они многое очень сильно упрощали. Например, в одной из разработок, контролировавшей процент искажений одновременно в 4 телеграфных каналах, в диалоговом режиме, с одновременным накоплением и выдачей результатов, и обслуживанием еще управляющего канала - аппарата “диспетчера”, чтобы уложиться в требуемую точность в +-1%, требовалось для худшего случая (если во время обработки прерывания от одного канала поступали прерывания от всех остальных 4), уложиться до 200мксек. Мне удалось обрабатывать самые худшие случаи максимум за 160! (на 580 процессоре с контроллером прерываний 580ВН59). Правда, пришлось поддержать аппаратно простенькой схемкой (на 555ЛП5), сравнивавшей текущее состояние с предыдущим, которое обработчик прерывания обновлял в специальном регистре, и также формировавшей запрос прерывания при каждом изменении полярности в любом из каналов. Мерялось смещение каждого перехoда в каждом знаке, с определением максимального отдельно по + и по - для каждого из каналов, с пересчетом в проценты. Тот контроллер до сих пор в работе, правда, прошивку в 573РФ5 пришлось один раз обновить (заряд стек). Сделал я его году в 86, а программа была около 3600 байт. Всего на плате могло быть установлено до 8кб(4шт РФ5 по 2к). ОЗУ было 4 кб.


  5. graskittism 18 Окт 2008 21:57

    Полностью согласен с мнением автора.


  6. testicq 17 Фев 2009 0:28

    Может кому пригодиться…
    Вот мои 10 байт кода, обнуляющие все регистры R00-R31:

    	LDI	ZL, 30	; +-----------------------+
    	CLR	ZH	; |			  |
    	DEC	ZL	; | Очистка РОН (R00-R31) |
    	ST	Z, ZH	; |	[10 байт кода]	  |
    	BRNE	PC-2	; +-----------------------+

    P.S. Работает на любых! МК AVR.
    P.P.S. Интересно - можно ли сделать еще короче?

    DI HALT

    Классно. Но я почему-то был свято убежден, что данный метод не работает. То ли у меня студия глюкнула и не показывала изменения регистров то ли еще что. Щас и не вспомню, но почему то тогда у меня с регистрами цикл не прокатил. Щас проверил - все ок.

    dima_m

    Мне пригодилось, уже применяю, проверил, работает. Круто спасибо.


  7. Andriy 09 Апр 2010 0:47

    Гонял код чистки SRAM, и заметил что она чистится не до конца, память по адресу RAMEND остается неизменной.
    По моему это связано этим:
    ST Z+,R16 // в Z=RAMEND-1, ячейку по адресу Z зачистили и cделали инкримент Z,

    CPI ZL,Low(RAMEND) // Z=RAMEND, проверили и опа вышли из цикла, а ячейку с адресом
    // RAMEND не почистили

    правильно будет так
    CPI ZL,Low(RAMEND+1)

    DI HALT

    Хм, действительно…

Оставьте свой отзыв

Вы должны войти, чтобы оставлять комментарии.


Материалы сайта являются авторскими. Копирование и публикация материалов без активной ссылки на первоисточник запрещено.

Реклама: