Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: XMega проблема при ожидании
Форум разработчиков электроники ELECTRONIX.ru > Сайт и форум > В помощь начинающему > MCS51, AVR, PIC, STM8, 8bit
Xeon
Всем доброго времени суток! Попытаюсь описать проблему... Использую AVR Studio 6. Нужно произвести несколько измерений с АЦП и потом их обработать.
Код инициализации:
Код
ADC_init();
            
            // запускаем цикл измерений
            FtransformFlush();
            Channel_number = ParseBuf[0];
            ADC_flush();
            ADC_start();
            
            // ожидаем завершения цикла измерений
            ADC_wait();

            // расчитываем модуль
            //FtransformCalcModuls();    
            
            // подготавливаем пакет к отправки    
            unsigned char Data[8];
            Data[0] = (unsigned char)  Fsumm0;
            Data[1] = (unsigned char) (Fsumm0 >> 8);
            Data[2] = (unsigned char) (Fsumm0 >> 16);
            Data[3] = (unsigned char) (Fsumm0 >> 24);        
            
            Data[4] = (unsigned char)  Fsumm1;
            Data[5] = (unsigned char) (Fsumm1 >> 8);
            Data[6] = (unsigned char) (Fsumm1 >> 16);
            Data[7] = (unsigned char) (Fsumm1 >> 24);        
            InsertPacket(0x02, Data, 8);

В данном коде важно запомнить функцию ADC_wait(); Она выглядит так:
Код
void ADC_wait()
{
    do {
    } while(Flags != ADC_CONVERSION_COMPLETE);
}


Данная функция ожидает завершения цикла измерений, здесь переменная Flags объявлена как volatile uint8_t Flags, ADC_CONVERSION_COMPLETE - константа равная 2, т.е. когда Flags будет равна 2 я выйду из бесконечного цикла и продолжу работу... Флаг устанавливается в прерывании, само прерывание:
Код
.global DMA_CH0_vect
DMA_CH0_vect:
        
        cli

        push r0; РЕГИСТР ПРОИЗВЕДЕНИЯ
        push r1; РЕГИСТР ПРОИЗВЕДЕНИЯ
        push r16; МНОЖИМОЕ
        push r17; МНОЖИТЕЛЬ
        push r4; СУММАТОР
        push r5; СУММАТОР
        push r6; СУММАТОР
        push r18; РЕГИСТР ЗНАКА ПРОИЗВЕДЕНИЯ
        push XL; УКАЗАТЕЛЬ СУММАТОРОВ
        push XH; УКАЗАТЕЛЬ СУММАТОРОВ
        push YL; УКАЗАТЕЛЬ СУММАТОРОВ
        push YH; УКАЗАТЕЛЬ СУММАТОРОВ
        push ZL; УКАЗАТЕЛЬ СТОЛБЦА ТАБЛИЦ
        push ZH; УКАЗАТЕЛЬ ТАБЛИЦЫ
        push r19
        /// push - 1, summ == 14


        [u][color="#FF0000"]lds r19, _SFR_IO_ADDR(PORTA_OUT)
        sbr r19, 16
        sts _SFR_IO_ADDR(PORTA_OUT), r19[/color][/u]

        clr r18

        // загружаем номер канала
        lds ZH, Channel_number /// 1
        inc ZH // инкрементируем так как таблицы начинаются с адресу 0х100 /// 1
        inc ZH /// 1
        // загружаем номер измерения
        lds ZL, Point_number /// 1
        /// 3

        // загружаем Fsumm0
        ldi XH, hi8(Fsumm0) /// 1
        ldi XL, lo8(Fsumm0) /// 1
        // загружаем Fsumm0
        ldi YH, hi8(Fsumm0) /// 1
        ldi YL, lo8(Fsumm0) /// 1
        // загружаем коэффициент
        lpm r17, Z /// 2
        // загружаем точку
        lds r16, Values /// 1
        muls r16, r17 /// 2
        // определение знака произведения
        sbrc r1, 7 /// 2
        // расширение знака на старший байт
        ser r18 /// 1
        // загрузка накопителя Fsumm0
        ld r4, X+ /// 2
        ld r5, X+ /// 2
        ld r6, X  /// 2
        // накопление Fsumm0
        add r4, r0 /// 1
        adc r5, r1 /// 1
        adc r6, r18 /// 1
        // созранение Fsumm0
        st Y+, r4 /// 2
        st Y+, r5 /// 2
        st Y, r6  /// 2
        /// 27



        clr r18

        // загружаем номер канала
        lds ZH, Channel_number /// 1
        ldi XL, 3
        add ZH, XL
        /// 2

        // загружаем Fsumm1
        ldi XH, hi8(Fsumm1) /// 1
        ldi XL, lo8(Fsumm1) /// 1
        // загружаем Fsumm1
        ldi YH, hi8(Fsumm1) /// 1
        ldi YL, lo8(Fsumm1) /// 1
        // загружаем коэффициент
        lpm r17, Z /// 2
        muls r16, r17 /// 2
        // определение знака произведения
        sbrc r1, 7 /// 2
        // расширение знака на старший байт
        ser r18 /// 1
        // загрузка накопителя Fsumm1
        ld r4, X+ /// 2
        ld r5, X+ /// 2
        ld r6, X  /// 2
        // накопление Fsumm1
        add r4, r0 /// 1
        adc r5, r1 /// 1
        adc r6, r18 /// 1
        // созранение Fsumm1
        st Y+, r4 /// 2
        st Y+, r5 /// 2
        st Y, r6  /// 2
        /// 26



        // инкрементируем номер точки и сохраняем
        inc ZL /// 1
        sts Point_number, ZL /// 1
        /// 2


        // если последнее измерение
        cpi ZL, 255 /// 1
        brne DMA_CH0_vect_end /// 2
            
            // stop ADC
            ldi r18, 0
            sts _SFR_IO_ADDR(ADCA_CTRLA), r18
            // reload to Point_number default value (0)
            sts Point_number, r18
            // set ADC_CONVERSION_COMPLETE flag
            ldi r18, 2
            sts Flags, r18
            ///

        DMA_CH0_vect_end:
        /// 3


        // DMA.CH0.CTRLB |= 48; // clear interrupt flags
        lds r19, _SFR_IO_ADDR(DMA_CH0_CTRLB) /// 1
        sbr r19, 0x30 /// 1
        sts _SFR_IO_ADDR(DMA_CH0_CTRLB), r19 /// 1
        /// 3

        [u][color="#FF0000"]lds r19, _SFR_IO_ADDR(PORTA_OUT)
        cbr r19, 16
        sts _SFR_IO_ADDR(PORTA_OUT), r19[/color][/u]    

        pop r19
        pop ZH
        pop ZL
        pop YH
        pop YL
        pop XH
        pop XL
        pop r18
        pop r6
        pop r5
        pop r4
        pop r17
        pop r16
        pop r1
        pop r0
        /// pop - 2, summ == 28

        sei

        reti /// 5

В коде прерывания, приведенном выше, важны строчки, которые я выделил красным цветом и подчеркнул. Эти строчки просто дёргают пином, нужно чтоб на осциле посмотреть сколько времени занимает одно измерение и вычисление...

Проблема возникает именно когда пытаюсь дергать ногами. Если убрать строки где дёргаю ногами, то работает так как и задумывалось: цикл преобразования завершается, передаем то что насчитали на комп для дальнейшей обработки. Когда же есть данные строчки (дёрганье ногами), происходит следующее: делается 3 измерения (в прерывание попадаю 3 раза, у меня надо сделать 256 измерений) после вдуг, непонятно из за чего, попадаю на конец функции ADC_wait() и после начинаю пытаться передать на комп невернeю инфу...
Дебажил я JtagICE'ом в момент когда я нахожусь в конwе функции ADC_wait() переменная Flag равна 1 и я должен находиться в бесконечном цикле, но выхожу из него... Из за чего это происходит?


Сточки, в которых дергаю ногой, выделить красным цветом и подчеркнуть не вышло) Это строчки в коде прерывания которые расположены сразу после push'ов и до pop'ов)

Есть у меня одна идея: может компилятор выделяет определённое количество памяти под стек и происходит его переполнение? Если это так, то может ктонить подскажет где можно посмотреть размер стека и поменять его в AVR Studio 6, а то я чет не чего такого не нашёл в свойствах проекта?

Не идея точно не подходит) Ведь количество вызовов одно и тоже!

В общем проблему решил простым перемещением кусков кода, а именно в коде прерывания поменял местами код где сбрасываю флаг DMA и код где выставляю пин в 0, т.е. зделал так:
Код
                             ...
                             ...
                             ...
                lds r19, _SFR_IO_ADDR(PORTA_OUT)
        cbr r19, 16
        sts _SFR_IO_ADDR(PORTA_OUT), r19


        // DMA.CH0.CTRLB |= 48; // clear interrupt flags
        lds r19, _SFR_IO_ADDR(DMA_CH0_CTRLB) /// 1
        sbr r19, 0x30 /// 1
        sts _SFR_IO_ADDR(DMA_CH0_CTRLB), r19 /// 1
        /// 3


        pop r19
        pop ZH
        pop ZL
        pop YH
        pop YL
        pop XH
        pop XL
        pop r18
        pop r6
        pop r5
        pop r4
        pop r17
        pop r16
        pop r1
        pop r0
        /// pop - 2, summ == 28

        sei

        reti /// 5


Но причины я так и не понял!!!!!!!!!! Люди может ктонить встречался/знает причину? Откройте тайну!)
ILYAUL
Xmega то какая?
И очень долгое прерывание , кроме флага вынесите всё в main
Xeon
XMega32 A4, здесь нет разницы особой, можно и в прерывании, вне прерывания в момент измерения не чего не должно происходить, пэтому для простоты реализации делается все в прерывании и при этом должна быть гарантия что все расчёты, которые делаются, будут сделаны до прихода нового измерения, а так же сами расчёты занимают почти всё процессорное время, а если делать всё за перыванием, то надо заводить дополнительные флаги, что требует дополнительные такты, а их к сожалению практически нет. Вот поэтому)
ILYAUL
Достаточно cli поставить в ответственных местах
Xeon
Но от доп. флагов не избавиться) а мне очень критично время выполнения, мне надо влезть в 128 тактов, между двумя измерениями...
V_G
1. А вы чего, регистр флагов проца (CPU_SREG) в прерывании не сохраняете? Очень неправильно!
2. Раз есть отладчик, посмотрите, NMI часом не разрешено?
3. Ну, и дерганье ногами тоже может вызвать прерывание. Надеюсь, оно запрещено?

4. Поддерживаю предыдущего оратора, длинная обработка прерывания не есть хорошо.
_Артём_
Цитата(V_G @ May 2 2012, 13:07) *
1. А вы чего, регистр флагов проца (CPU_SREG) в прерывании не сохраняете? Очень неправильно!

Неправильно - не то слово. Глючить будет по страшному (кроме случая если main пустой и нет других прерываний).

Цитата(V_G @ May 2 2012, 13:07) *
4. Поддерживаю предыдущего оратора, длинная обработка прерывания не есть хорошо.

А если в программе нет ничего другого? То тогда лучше сразув прерывании всё и обработать.
V_G
Цитата(_Артём_ @ May 3 2012, 06:38) *
А если в программе нет ничего другого? То тогда лучше сразув прерывании всё и обработать.

Ну, там DMA еще есть зачем-то (не разбирался). Уже непустой main как минимум.
А если в программе кроме прерывания ничего нет, можно обойтись и без прерывания, опрашивая соотв. флаг в бесконечном цикле
Xeon
Как правильно сохранить регистр статуса проца? Загрузить какой-нибудь lds, а потом сохранить где-нибудь (например в стеке) и в конце загрузить sts?

По поводу почему длинное прерывание уже написал... Конечно я понимаю, что много кода в прерывании - НЕ ЕСТЬ ХОРОШО, но мне надо влезть в 128 тактов проца между измерениями...
V_G
Цитата(Xeon @ May 3 2012, 16:23) *
Как правильно сохранить регистр статуса проца?

CODE
SPI_DMA_int:
PUSH ac_tmp
IN ac_tmp,CPU_SREG
PUSH ac_tmp
;тут процедура обработки прерывания


POP ac_tmp
OUT CPU_SREG,ac_tmp ;restore Status register
POP ac_tmp
RETI

В качестве ac_tmp используется один из РОН.
Учтите, что в CPU_SREG сидит и флаг I, потому при восстановлении статуса прерывания автоматически разрешатся.

Xeon
Цитата(V_G @ May 3 2012, 09:04) *
CODE
SPI_DMA_int:
PUSH ac_tmp
IN ac_tmp,CPU_SREG
PUSH ac_tmp
;тут процедура обработки прерывания


POP ac_tmp
OUT CPU_SREG,ac_tmp ;restore Status register
POP ac_tmp
RETI

В качестве ac_tmp используется один из РОН.
Учтите, что в CPU_SREG сидит и флаг I, потому при восстановлении статуса прерывания автоматически разрешатся.


Пасиб!!!! Дело было в регистре статуса!) Терь буду знать!
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.