Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: MSP430 - снова вопросы от чайника
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > MSP430
Страницы: 1, 2, 3, 4, 5, 6
Daria
smile.gif Здравствуйте. Кто читал предыдущую мою тему "MSP430 - вопросы от чайника", тот поймет - это снова я biggrin.gif Тему закрыли, слишком долго висела. А вопрсов все больше. smile.gif
Если кому-то вдруг окажется не влом разбираться, то
Вот кусок проги:
int flag;//глобальные переменные
int cindex;
испульзуемые функции(write_flash - пишет число во flash, read_flash - считывает)
void write_flash(float* value, float *addr)
{ while(FCTL1 & BUSY);
_BIC_SR(GIE);
FCTL1 = FWKEY + ERASE;
FCTL3 = FWKEY;
*addr = 0;

while(FCTL1 & BUSY);
FCTL1 = FWKEY + WRT; for (int i=0;i<=3;i++)
{ *addr = value[i];
addr++;
}

_BIS_SR(GIE);
while(FCTL1 & BUSY);
FCTL1 = FWKEY;
FCTL3 = FWKEY + LOCK;
}

void read_flash(float* addr, float* pfO)
{
for (int i=0;i<=3;i++)
{ pfO[i]=*addr;
addr++;
}
}
Чего хочу сделать - Если присылаю 0, то должны считаться два показания с выводов P6.3 и P6.4, присылается 0хАА - т.е. "считалось", записываются первые элементы двух массивоы данных.
Когда набирается по четыре элемента, индекс сбрасывается
Если присылаю 0xFF, то должна выполнится функция calibrovka_hmc, если она выполняется неверно, то присылается 0хСС, иначе из flash считывается и присылается вычисленная с помощью этой функции константа.
Внутри функции main
ADC12CTL0 = ADC12ON + REFON + SHT0_8; //настройка АЦП
ADC12CTL1 = CSTARTADD0 + CSTARTADD1 + ADC12SSEL_1+ CONSEQ_1;
ADC12MCTL3 = INCH_3;
ADC12MCTL4 = INCH_4 + EOS;

float temp[4];
flag=0;
cindex=0;
for(;;)
{
for(delay = 0; delay <20000; delay++);
if( flag==0 )
{send_int(0x0);
}
if( flag==1 )
{

if (calibrovka_hmc(CVx, CVy, temp)==0)//calibrovka_hmc - имеет параметрами два массива и вычисляет четыре константы(нужные для калибровки устройства)

{
send_int(0xCC);
flag=0;
}
else
{
float pfO[4];
read_flash((float*)0x1000,pfO);
r=(int)(pfO[0]+0.5);
send_int®;
flag=0;
}

flag=4;
}

if (flag==2)
{
ADC12CTL0 |=ADC12SC + ENC; CVx[cindex] = ADC12MEM3; CVy[cindex] = ADC12MEM4;
ADC12CTL0 &= ENC;
cindex++;
send_int(0xAA);
flag=0;
}

if (cindex>3)
cindex=0;
}
}
#pragma vector=UART0RX_VECTOR
__interrupt void usart0_rx ( void)
{
if( RXBUF0==0xFF)
flag = 1;
if (RXBUF0==0x0)
flag = 2;
}
В ЧЕМ ПРОБЛЕМА:
Перед выполнением калибровки присылаю элементы массива, полученного при считывании показаний с выводов, - оказывается, что в массив записываются по два-три раза одно значение, хотя этого не может быть. Соответственно, из-за этого не работает и все остальное crying.gif
Может, неправильно настроен АЦП? Или в чем может быть дело?
И, если кому-то вдруг не влом разбираться - функции записи во flash и чтения flash - можно ли сделать как-то поумнее?
rezident
Некоторое (продолжительное) время не заглядывал в форум, а когда заглянул оказалось, что вашу тему прикрыли. cranky.gif
По поводу Flash.
Проверять готовность к записи во Flash (бит BUSY) при выполнении программы записи прямо из Flash нет необходимости. Вот такая вот продвинутая технология у MSP430 smile.gif Почему так, объяснять еще раз не буду. Читайте внимательно User's Manual. Проверка готовности нужна только в том случае, если ваша функция записи исполняется из ОЗУ (SRAM), а не из Flash.
Запись во внутреннюю Flash MSP430 может производится 16-и разрядными словами и/или байтами. float имеет размерность 32 бита, т.е. 2 слова/4 байта. Так что, для записи переменной float нужно как минимум две команды записи 16-и битных слов или четыре команды записи байта. У вас указатели value и addr указывают на тип float. Поэтому команды типа addr++; увеличивают значение такого указателя на 4 байта и во Flash пишется какая-то фигня. Пишите тогда уж пословно, делая явное приведение типа к 16-и битному целому.
Код
for (int i=0; i<(4*2); i++) // здесь я явно выделил удвоение числа элементов записи
((unsigned short *)addr)[i] = ((unsigned short *)value)[i];

Назначение функции чтения из Flash я вообще не очень понимаю. У MSP430 фон-Неймановская архитектура с общим адресным пространством для команд и данных. Встроенного EEPROM тоже нет. Так что выделять копирование в отдельную функцию (если только она не вызывается несколько раз) я не вижу смысла. Да и ее тоже можно оптимизировать на одну команду (выкинув addr++; ), если в теле цикла писать
Код
pfO[i]=addr[i];

Насчет всего остального.
Может у меня сложилось неверное впечатление, но похоже вы не осознаете, что такое синхронные и асинхронные события. Просто не владеете таким уровнем абстракций. Попробую объяснить на бытовом примере.
Вы когда-нибудь видели как подъемный кран работает? Внизу на земле стоит стропальщик, который крепит к крюку крана груз, а наверху в кабине крана сидит крановщик, который управляет движением крюка, стрелы и платформы крана. Причем крановщик не сам-по-себе управляет, а подчиняется управляющим командам стропальщика. Только после того как груз
- прицеплен,
- надежность крепления проверена,
- стропальщик подал команду "вира!"
крановщик включает двигатель, приводящий к движению крюка и стрелы крана. До подачи команды "вира!" крановщик просто ждет готовности груза.
У вас в системе присутствуют асинхронные процессы (измерение АЦП, прием/передача UART), а вы работаете с ними как Бог на душу положит. sad.gif Стропальщик убил бы просто такого крановщика, если бы остался жив после его несанкционированных действий. wink.gif
Эта иллюстрация синхронных действий. Для иллюстрации же асинхронных процессов к картине работы башенного крана нужно добавить несколько КАМАЗов с кирпичем, которые приезжают на стройку асинхронно работе крана, когда у них получится, и сваливают кирпич в общую кучу. Которую потом стропальщик с крановщиком общими синхронными действиями перемещают на строящийся уровень дома smile.gif
Эта моя пространная тирада относится в частности к строке
Цитата
ADC12CTL0 |=ADC12SC + ENC; CVx[cindex] = ADC12MEM3; CVy[cindex] = ADC12MEM4;

Где вы запускаете преобразование АЦП и тут же, не дожидаясь окончания выполнения преобразования, считываете результаты оцифровки непонятно чего. Раз вы не используете прерывание от АЦП, ну подождите для приличия установки флага готовности что ли laughing.gif
Код
ADC12CTL0 |=ADC12SC|ENC;
while ((ADC12IFG&(1<<3))!=0);
CVx[cindex]=ADCMEM3;
while ((ADC12IFG&(1<<4))!=0);
CVy[cindex] = ADC12MEM4;

То же самое относится к модификации флага в прерывании UART. Если вы посылаете последовательности из двух команд 0xFF и 0x00, и снова 0xFF и 0x00, но не успели считать значение какого-то флага, то его значение потеряется и вы получите дублирование команды 0xFF или 0x00. Потому, что прием через UART асинхронен выполнению основного тела программы, где эти флаги анализируются. Для того чтобы команды, принятые через UART не терялись, их нужно буферизировать, т.е. складывать в некий массив из которого в основном теле программы они будут извлекаться.
Буферы бывают линейные (типа FIFO) и циклические. По-моему для вашего случая больше подходит циклический буфер. Пример.
Код
#define UART_MAXBUFSIZE 8  //здесь задается размер выделяемого UART буфера
struct cbuf
{ unsigned char *pBuf;  //указатель на начало буфера
  unsigned int rIdx;   //индекс для чтения элемента из буфера
  unsigned int wIdx;  //индекс для записи элемента в буфер
  unsigned int cntr;  //счетчик общего количества элементов в буфере, чтобы знать о его заполнении
} cbuf;
unsigned char uartBuf[UART_MAXBUFSIZE]; // собственно сам буфер UART


где-то в начале программы нужно инициализировать структуру этого буфера
Код
...
cbuf.pBuf=&uartBuf[0];
cbuf.rIdx=0;
cbuf.wIdx=0;
cbuf.cntr=0;
...

В прерывании от приемника UART запись в буфер
Код
#pragma vector=UART0RX_VECTOR
__interrupt void usart0_rx ( void)
{ if (cbuf.cntr<UART_MAXBUFSIZE)
  { cbuf.pBuf[cbuf.wIdx]=U0RXBUF;
    if (cbuf.wIdx<(UART_MAXBUFSIZE-1))
      cbuf.wIdx++;
    else
      cbuf.wIdx=0;
    cbuf.cntr++;
  }
}

В теле программы считывание из буфера. Для атомарности доступа к буферу на время доступа к нему временно запрещаем прерывание от приемника UART
Код
...
if (cbuf.cntr>0)
{ IE1&=~URXIE0;
  uCmd=cbuf.pBuf[cbuf.rIdx];
  cBuf.cntr--;
  IE1|=URXIE0;
  if (cbuf.rIdx<(UART_MAXBUFSIZE-1))
    cbuf.rIdx++;
  else
    cbuf.rIdx=0;
}
// дальше значение переменной uCmd можно использовать как текущую команду, полученную через UART
...
Daria
Ага, все же заглянули smile.gif спасибо за комментарии и критику, все подробно и пространно. smile.gif
По поводу АЦП я уже сама доперла, установку флагов жду, режим использую последовательный, и бит MSC ставлю biggrin.gif Теперь работает. По поводу read_flash - ну да, но это так, для понта отдельная функция smile.gif
А по поводу write_flash - хм. но она работает. я проверяла, во flash пишется не фигня smile.gif может это мне просто везет? biggrin.gif
Про буфер - спасибо, попробую.
Вообще спасибо, что не забыли smile.gif
=DS=
Цитата(Daria @ Jul 24 2008, 23:29) *
А по поводу write_flash - хм. но она работает. я проверяла, во flash пишется не фигня smile.gif может это мне просто везет?


Пока везет. В данном случае строка:

*addr = value[i];
скорее всего, транслируется компилятором как

MOV.W @R12+, 0(R13)
MOV.W @R12+, 2(R13)


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

*addr = 0;

транслируется в

MOV.W #0x0, 0(R13)
MOV.W #0x0, 2(R13)


что два раза подряд вызовет стирание сегмента, причем без пауз между ними. В лучшем случае это вызовет удвоенный износ флешки, в худшем - неизвестно (поскольку до подобных действий наверняка еще никто не додумался smile.gif )
Daria
Цитата(=DS= @ Jul 25 2008, 07:03) *
*addr = 0;

транслируется в

MOV.W #0x0, 0(R13)
MOV.W #0x0, 2(R13)


что два раза подряд вызовет стирание сегмента, причем без пауз между ними. В лучшем случае это вызовет удвоенный износ флешки, в худшем - неизвестно (поскольку до подобных действий наверняка еще никто не додумался smile.gif )

sad.gif а как тогда написать? Такая строка была в примере, который я скачала с ИАРовского сайта, - "фиктивная запись". Как тогда написать фиктивную запись ?smile.gif
И вот еще ВОПРОС- понимаю, что, наверное, глупый, но -
Когда гружу прогу с помощью IAR Kickstart - работает нормально, считает четко все - я проверяла. Но так как код уже превышает 4кБ, гружу с помощью полной версии - на COM приходит полная ересь. С чем это может быть связано? Код такой же - один в один.
Я думала, может дело в доступе к библиотекам, но не ругается же на код, на #include <math.h>, работает, считает, пишет... только фигню 05.gif
Вот. Не ругайтесь! - уж такая я глупая biggrin.gif
Вот сама функция, она принимает два массива и на их основании вычисляет и пишет во flash 4 четыре константы
int calibrovka_hmc( float* V1, float* V2, float* calibr)
{

float x[4], a[3][3],d[4];
float Ox,k,Oy,kx,ky;
for (int i=0;i<4;i++)
{ a[i][0]=2*(V1[i]-V1[i+1]);
a[i][1]=2*(V2[i]-V2[i+1]);
a[i][2]= -((V2[i])*(V2[i])-(V2[i+1])*(V2[i+1]));
a[i][3]= ((V1[i])*(V1[i])-(V1[i+1])*(V1[i+1]));
d[i]=((V1[i])*(V1[i])-(V1[i+1])*(V1[i+1]));
}

if (gauss(x,a,d,3)==0) return 0;
else
{
Ox=x[0];
k=x[2];
if (x[2]!=0) Oy=x[1]/x[2];
else Oy=0;
}
kx=sqrt((V1[3]-Ox)*(V1[3]-Ox)+k*(V2[3]-Oy)*(V2[3]-Oy));
ky=sqrt((kx)*(kx)/k);
calibr[0]=Ox;
calibr[1]=Oy;
calibr[2]=kx;
calibr[3]=ky;
write_flash( calibr, (float*) 0x1080);
return 1;
}

gauss( ) - решение системы методом Гаусса, она точно работает, не буду приводить - к тому же вряд ли кому-то будет охота в ней разбираться smile.gif
Такие вопросы laughing.gif
rezident
Цитата(Daria @ Jul 26 2008, 00:27) *
sad.gif а как тогда написать? Такая строка была в примере, который я скачала с ИАРовского сайта, - "фиктивная запись". Как тогда написать фиктивную запись ?smile.gif
Конечно фиктивная запись, вот только в примере скорее всего был указатель другого типа (unsigned int или unsigned char). Как поступить в данной ситуации я вам уже выше подсказал - используйте явное приведение типа. Раз у вас указатель на float, то при фиктивной записи преобразуйте его к указателю на unsigned int.
Код
*((unsigned int *)addr)=0;

Цитата(Daria @ Jul 26 2008, 00:27) *
И вот еще ВОПРОС- понимаю, что, наверное, глупый, но -
Когда гружу прогу с помощью IAR Kickstart - работает нормально, считает четко все - я проверяла. Но так как код уже превышает 4кБ, гружу с помощью полной версии - на COM приходит полная ересь. С чем это может быть связано? Код такой же - один в один.
Версии IAR одинаковы? Настройки проекта одинаковы? И вообще, спрашивается зачем использовать KickStart при наличии полной версии? 07.gif Если вы не пользуетесь дебаггером, то для загрузки прошивки в кристалл использовать IAR вообще не обязательно. Пользуйтесь специальными программами. MSPFET Programmer от Kurt-а или FET-Pro430 Lite от Elprotronic или вот тут же в форуме zltigo свою утилиту AT430 предлагает.
А вообще правильность работы алгоритма программы можно проверить в симуляторе. Тем более, если у вас есть в наличии результаты работы алгоритма по известным данным.
Daria
И версии, и настройки. А кикстарт - потому что сначала был только он smile.gif
FET-Pro430 Lite у меня есть. но не очень-то удобно компилироваться, а потом грузить через нее - проще и быстрее же все делать в ИАРе smile.gif
Дебаггером, кстати, пользуюсь, но проверить алгоритм не получается - он не показывает текущие значения моих переменных, только регистры контроллера 05.gif - то же касается симулятора

Кстати, если настройка АЦП
ADC12CTL0 = ADC12ON +REFON + SHT0_8+MSC;
ADC12CTL1 = CSTARTADD0 + CSTARTADD1 + ADC12SSEL_1+ CONSEQ_1+SHP;
ADC12MCTL3 = INCH_3;
ADC12MCTL4 = INCH_4 + EOS;
а потом
ADC12CTL0 |=ADC12SC;
ADC12CTL0 |=ENC;
while(ADC12IFG & BIT3);
CVx[cindex] = ADC12MEM3;
while(ADC12IFG & BIT4);
CVy[cindex] = ADC12MEM4;
ADC12CTL0 &= ENC;
так нормально? Или опять грузовики с кирпичами понаехали? biggrin.gif
rezident
Цитата(Daria @ Jul 26 2008, 03:05) *
И версии, и настройки. А кикстарт - потому что сначала был только он smile.gif
FET-Pro430 Lite у меня есть. но не очень-то удобно компилироваться, а потом грузить через нее - проще и быстрее же все делать в ИАРе smile.gif
Не проще. C-Cpy грузит "дебажную" версию прошивки, а в реальное устройство вы будете компилировать другую версию, без дебаговских примочек. Не нравится FET-Pro430, используйте программатор Курта. Её (программу) кстати можно интегрировать в IAR, т.к. в ней имеется поддержка командной строки. Tools->Configure Tools->New обзываете ее и заполняете необходимые поля параметрами запуска программы MSPFET Programmer. Только для нее нужно использовать Release-ную настройку с форматом выходного файла TI-TXT или HEX.
Цитата(Daria @ Jul 26 2008, 03:05) *
Дебаггером, кстати, пользуюсь, но проверить алгоритм не получается - он не показывает текущие значения моих переменных, только регистры контроллера 05.gif - то же касается симулятора
Ну здрасте! А ручками в окно View->Watch имя интересуемых переменных или структур забить лень что ли? Или окно View->Locals открыть и наблюдать за автоматическими переменными.
Цитата(Daria @ Jul 26 2008, 03:05) *
Кстати, если настройка АЦП
ADC12CTL0 = ADC12ON +REFON + SHT0_8+MSC;
ADC12CTL1 = CSTARTADD0 + CSTARTADD1 + ADC12SSEL_1+ CONSEQ_1+SHP;
ADC12MCTL3 = INCH_3;
ADC12MCTL4 = INCH_4 + EOS;
а потом
ADC12CTL0 |=ADC12SC;
ADC12CTL0 |=ENC;
while(ADC12IFG & BIT3);
CVx[cindex] = ADC12MEM3;
while(ADC12IFG & BIT4);
CVy[cindex] = ADC12MEM4;
ADC12CTL0 &= ENC;
так нормально? Или опять грузовики с кирпичами понаехали? biggrin.gif
А зачем вы включаете опору, если ее не использует е при измерении? А так... сойдет smile.gif Только я бы перед запуском преобразования флаги ADC12IFG почистил бы на всякий пожарный.
Опс! Тока сейчас заметил, неверно все-таки. Нужно так.
while((ADC12IFG & BIT3)==0);
Пока флаг не установлен, нужно ждать. А у вас инверсное условие. В моем примере выше я тоже ошибся, но поправить уже не могу. sad.gif
=DS=
Цитата(Daria @ Jul 26 2008, 01:05) *
Дебаггером, кстати, пользуюсь, но проверить алгоритм не получается - он не показывает текущие значения моих переменных, только регистры контроллера 05.gif - то же касается симулятора

Галочка в project->options->linker->output "debug information for C-Spy" стоит?
Daria
Цитата(rezident @ Jul 26 2008, 01:38) *
Ну здрасте! А ручками в окно View->Watch имя интересуемых переменных или структур забить лень что ли? Или окно View->Locals открыть и наблюдать за автоматическими переменными.

rezident, ну вы меня уж за совсем-пресовсем тупую держите! 05.gif smile.gif Конечно, не лень! Но состояние переменных почему-то не отображается, вместо этого error стоит. Только состояние регистров. Я думала, это так всегда biggrin.gif

Цитата(rezident @ Jul 26 2008, 01:38) *
Опс! Тока сейчас заметил, неверно все-таки. Нужно так.
while((ADC12IFG & BIT3)==0);
Пока флаг не установлен, нужно ждать. А у вас инверсное условие. В моем примере выше я тоже ошибся, но поправить уже не могу. sad.gif

не, это я тоже ошиблась, когда вам писала - на самом деле я, конечно, while(!(ADC12IFG & BIT3)) пишу. Ага? Тож на тож ведь. или все же не очень корректно? smile.gif



Цитата(=DS= @ Jul 26 2008, 01:40) *
Галочка в project->options->linker->output "debug information for C-Spy" стоит?

А вот не знаю. Посмотрю, спасибо smile.gif
rezident
Цитата(Daria @ Jul 26 2008, 18:38) *
rezident, ну вы меня уж за совсем-пресовсем тупую держите! 05.gif smile.gif
Ну зачем же сразу такие эпитеты? Я просто делаю скидку на женскую альтернативную логику. Извините, если обидел.
Цитата(Daria @ Jul 26 2008, 18:38) *
Но состояние переменных почему-то не отображается, вместо этого error стоит. Только состояние регистров. Я думала, это так всегда biggrin.gif
=DS=, указал вероятную причину.
Цитата(Daria @ Jul 26 2008, 18:38) *
не, это я тоже ошиблась, когда вам писала - на самом деле я, конечно, while(!(ADC12IFG & BIT3)) пишу. Ага? Тож на тож ведь. или все же не очень корректно? smile.gif
Да пишите себе на здоровье, раз вам такой стиль нравится! Просто я лично при проверке условий предпочитаю явное сравнение с нулем. Для меня так нагляднее и понятнее. Но в свою "религию" я никого насильно не обращаю biggrin.gif
Daria
Так вот. Галочка стоит. А состояние переменных все равно error 05.gif
По поводу ошибок в программе - у меня, кстати, все-таки разные версии, Kickstart 5.0, a Evolution 4.0. smile.gif Но разве это может повлиять.? Ведь не ругается, все считает... Что-то не так с #include <math.h>? А, еще - они стоят вместе. Могут, перепуться библиотеки?
Теперь такие вопросы:
rezident, вы вот давно обещали помочь разобраться с таймером wink.gif Так, как я это делаю(по переключению флага ) - не пойдет biggrin.gif "Камазы с кирпичами", да и только biggrin.gif При большой скорости пересчета, в принципе, можно забить на некоторые неточности, но, когда нужно выполнять что-то четко по команде...
Вы говорили, что можно использовать выход таймера. Как? smile.gif Ведь мне не нужно постоянно переключать вывод, нужно долго держать его в единице, а потом короткое время в ноле.
Еще- если я запущу еще и таймер В - это не будет излишней перегрузкой программы? Дело в том, что мне надо постоянно фиксировать состояние переменной azimut и где-то раз в секунду смотреть, не изменилась ли она, если изменилась - отсылать команду на COM. Что, если делать это по таймеру В?
Короче - помогите, спасите smile.gif
rezident
Цитата(Daria @ Jul 29 2008, 02:14) *
Так вот. Галочка стоит. А состояние переменных все равно error 05.gif
Ну я не знаю. laughing.gif Попробовал я в своем текущем проекте убирать "птички" в опциях проекта везде, где только встретил "Generate debug ...". В режиме отладки пропала возможность "шагать" по Сишному исходнику. Только по ASM-овому тексту теперь шагает. Но значения глобальных переменных в окне Watch и локальных переменных в окне Locals все равно показывает. Может вы пытаетесь в окне Watch значение локальной переменной посмотреть? Тогда понятно почему ошибку выдает.
Цитата(Daria @ Jul 29 2008, 02:14) *
По поводу ошибок в программе - у меня, кстати, все-таки разные версии, Kickstart 5.0, a Evolution 4.0. smile.gif Но разве это может повлиять.? Ведь не ругается, все считает... Что-то не так с #include <math.h>? А, еще - они стоят вместе. Могут, перепуться библиотеки?
В один каталог крайне не рекомендуется устанавливать разные версии одного продукта. Если только с одинаковой версией IDE, но для разных целевых платформ (например, EWAVR и EW430). Устанавливайте версии KickStart и Evalution в разные каталоги, тогда путаницы не будет.
Цитата(Daria @ Jul 29 2008, 02:14) *
rezident, вы вот давно обещали помочь разобраться с таймером wink.gif Так, как я это делаю(по переключению флага ) - не пойдет biggrin.gif "Камазы с кирпичами", да и только biggrin.gif При большой скорости пересчета, в принципе, можно забить на некоторые неточности, но, когда нужно выполнять что-то четко по команде...
Вы говорили, что можно использовать выход таймера. Как? smile.gif Ведь мне не нужно постоянно переключать вывод, нужно долго держать его в единице, а потом короткое время в ноле.
У таймеров MSP430 имеется режим compare (сравнения), в котором содержимое регистра TAR/TBR (которое постоянно инкрементируется по тактовым импульсам TACLK/TBCLK) сравнивается с capture-регистрами CCRx. При совпадении CCRx и TAR/TBR происходит установка флага CCIFG в соответствующем регистре CCTLx, а если разрешено прерывание для этого регистра (установлен бит CCIE), то еще и генерируется вызов прерывания по соответствующему вектору. Векторов для каждого таймера по две штуки. Один для CCR0, второй вектор расшарен для всех остальных CCRx. Манипулируя содержимым CCRx, вы можете генерировать прерывания для разных отметок времени. Кроме этого, для каждого блока compare имеется выходной модуль, который по возникающим событиям таймера может управлять состоянием бита OUT в CCTLx и ассоциированного с ним пина (или пинов, т.к. для TimerA эта функция выведена на несколько пинов) TAx/TBx. По событию совпадения TAR/TBR и CCRx бит OUT может быть сброшен, установлен, проинвертирован. Совпадение TAR/TBR и CCR0 действует на состояния всех битов OUT во всех выходных модулях таймера. По этому событию (от CCR0) бит OUT также может быть сброшен, установлен, проинвертирован. Таким образом можно генерировать ШИМ. Например, если для TA1 выбрать режим выходного модуля "сброс/установка", то по совпадению TAR и CCR1 вывод TA1 сброситься в лог.0, а по совпадению TAR и CCR0 установиться в лог.1. Режим выходного блока задается для каждого регистра индивидуально. Исключение составляет только CCR0, т.к. он действует сам на себя дважды за период, если выбран режим счета CountUp или Up/Down в которых период счета определяется содержимым этого CCR0.
Я предлагал вам использовать TimerA в режим счета Continious (с переполением). Выходной модуль TA0 или TA1 использовать для генерации выходного управляющего импульса, который одновременно запускает преобразование АЦП в непрерывном режиме последовательности каналов. Вам нужно только в прерывании от CCR0 или CCR1 вести счетчик и при достижении предпоследнего интервала отсчета импульса перепрограммировать режимы выходного блока и вовремя остановить АЦП для нового запуска по другому фронту TA0 или TA1.
Вот. Утомился я что-то "на пальцах" пересказывать содержимое User's Manual. smile.gif Если еще непонятно, то пояснения будут уже завтра.
Кстати, не помню, давал ли я ссылку на русскоязычный переводной вариант User's Manual? На всякий случай продублирую ссылку на Компэловскую библиотеку.
Цитата(Daria @ Jul 29 2008, 02:14) *
Еще- если я запущу еще и таймер В - это не будет излишней перегрузкой программы? Дело в том, что мне надо постоянно фиксировать состояние переменной azimut и где-то раз в секунду смотреть, не изменилась ли она, если изменилась - отсылать команду на COM. Что, если делать это по таймеру В?
Нет особой разницы на какой таймере вести счет временных отметок (пауз). Если вам так удобно, то задействуйте TimerB. Хотя необходимости в этом я не вижу. У вас уже имеется счетчик на TimerA. Просто добавьте в процедуру прерывания еще один счетчик с удобным весовым коэффициентом. Я предпочитаю вести счет времени в миллисекундах. Например, используя WDTimer и ACLK от часового таймера.
Код
#define SYSTIME_TICKPERIOD_MS  16 //весовой коэффициент системных тиков

volatile unsigned int TMTick;

//================================================//
// Инициализация таймера системных тиков          //
//------------------------------------------------//
//аргументы :нет                                  //
//возвращает:нет                                  //
//================================================//
void initSysTickTimer(void)
{ TMTick=0;
  WDTCTL=WDT_ADLY_16;                             //интервальный таймер тиков, тик=15,625мс
  IFG1&=~WDTIFG;                                  //сбросим флаг
  IE1|=WDTIE;                                     //разр. прерывание
}

const unsigned int SysTickCorrTbl[8]={0,0,1,0,0,1,0,1};  //корректировочная таблица

//================================================//
// Обработчик прерываний системного таймера       //
//------------------------------------------------//
//аргументы :нет                                  //
//возвращает:нет                                  //
//================================================//
#pragma vector=WDT_VECTOR
#pragma type_attribute=__interrupt
void SysTickTimer_ISR(void)
{ static unsigned int sysCntr=0;                     //вспомогательная переменная сист.тиков
//--- системные тики [мс] ---
  if (sysCntr<7)
    sysCntr++;
  else
    sysCntr=0;
  TMTick+=SYSTIME_TICKPERIOD_MS-SysTickCorrTbl[sysCntr];//инкремент сист. тиков (мс)
}

В основном теле программы я беру абсолютную разницу (беззнаковую разность) между двумя считанными значениями переменной TMTick и получаю прошедший интервал времени. Учитывая необходимость атомарности доступа к переменной системных тиков, выбираю ее тип unsigned int. Этот тип позволяет определять интервал времени до 65,5 секунд (период переполнения переменной). Если нужны бОльшие интервалы времени, то нужно брать 32-х битную переменную, но тогда придется запретом/разрешением прерывания или другими способами обеспечить атомарность доступа к ней.
Еще замечание насчет примененной корректировочной таблицы. Поскольку период частоты, полученной от деления частоты часового кварца (32768Гц) на делитель WDTimer (512), не является целой величиной миллисекунд (15,625мс), то я корректирую точность системных тиков с помощью таблицы. И на периоде 250мс получаю целое число миллисекунд.
Daria
Цитата(rezident @ Jul 29 2008, 03:05) *
Ну я не знаю. laughing.gif Попробовал я в своем текущем проекте убирать "птички" в опциях проекта везде, где только встретил "Generate debug ...". В режиме отладки пропала возможность "шагать" по Сишному исходнику. Только по ASM-овому тексту теперь шагает. Но значения глобальных переменных в окне Watch и локальных переменных в окне Locals все равно показывает. Может вы пытаетесь в окне Watch значение локальной переменной посмотреть? Тогда понятно почему ошибку выдает.

Ничего подобного smile.gif Все же не совсем, не совсем я даунито, я уже говорила. Локальные переменные смотрю, где надо smile.gif
Об остальном - rezident, какой же вы все-таки хороший! biggrin.gif Так все подробно и хорошо. Та функция, которую хотела сделать, вроде работает, а про таймеры... В принципе я пыталась сделать нечто подобное, но возникли проблемы smile.gif Какие именно, напишу чуть позже, когда еще раз попробую переделать, руководствуясь вашими комментариями smile.gif Спасибо, конечно, огромное. буду разбираться smile.gif
Daria
Здравствуйте, снова я smile.gif
Цитата(rezident @ Jul 29 2008, 03:05) *
Например, если для TA1 выбрать режим выходного модуля "сброс/установка", то по совпадению TAR и CCR1 вывод TA1 сброситься в лог.0, а по совпадению TAR и CCR0 установиться в лог.1.
...Я предлагал вам использовать TimerA в режим счета Continious (с переполением). Выходной модуль TA0 или TA1 использовать для генерации выходного управляющего импульса, который одновременно запускает преобразование АЦП в непрерывном режиме последовательности каналов. Вам нужно только в прерывании от CCR0 или CCR1 вести счетчик и при достижении предпоследнего интервала отсчета импульса перепрограммировать режимы выходного блока и вовремя остановить АЦП для нового запуска по другому фронту TA0 или TA1.

Все же я какая-то непонятливая sad.gif допустим, режим Continious и режим "сброс/установка", по совпадению TAR и CCR1 вывод TA1 сброситься в лог.0, а по совпадению TAR и CCR0 установиться в лог.1. Но максимальное число. которое я могу записать в CCR0 - FFFF. А мне нужно держать лог. 1 долго. Как быть? Может, установить сначала режим "установка", в прерываниях от CCR0 вести счетчик, а в последнем переключить режим на "сброс". В следующем за ним прерывании опять "установка". Можно так сделать? Вы об этом говорите? smile.gif
Да, а по поводу версий IAR - дело было просто в глючности самой ломаной версии 3.2 smile.gif
rezident
Цитата(Daria @ Aug 8 2008, 02:43) *
ЗКак быть? Может, установить сначала режим "установка", в прерываниях от CCR0 вести счетчик, а в последнем переключить режим на "сброс". В следующем за ним прерывании опять "установка". Можно так сделать? Вы об этом говорите? smile.gif
Да, режим сброс/установка только для формирования сигнала длительность которого меньше периода перезагрузки таймера и для более точного формирования времянки сигнала. Хотя. если задержка на вход в прерывание от таймера у вас детерминирована и по времени вполне устраивает, то вовсе не обязательно формировать сигнал аппаратно. Можно оставить все как у вас сейчас уже работает. И запуск АЦП делать прямо в таймерном прерывании.
Daria
Цитата(rezident @ Aug 8 2008, 05:29) *
Можно оставить все как у вас сейчас уже работает. И запуск АЦП делать прямо в таймерном прерывании.

Сейчас не очень хорошо. Понадобилось вот стыковаться, а у меня с синхронностью выявились большие проблемы smile.gif Надо, чтобы все было четко-пречетко. И без камазов с кирпичами biggrin.gif То есть, можно сделать так, как я говорю?
а вот по поводу АЦП:
Мне сказали, что цикл while(ADC12IFG & BIT3); - это некорректное использование процесссора. smile.gif типа, чего он будет столько тиков в пустую ждать. Надо работать с прерываниями. и вот вопрос -
Прерывания. как я понимаю, приходят после установки флагов ADC12IFGx, которые устанавливаются после заполнения ADCMEMx. Если режим повторно-последовательный, то прерывания будут после обработки каждого канала?
А вот в найденном примере в прерывании пишется
static unsigned int index;
resault[index] = ADC12MEM0;
resault[index] = ADC12MEM1;
resault[index] = ADC12MEM2;
index=(index+1)%8;
как же так? smile.gif ADCMEM0 заполнен. пришло прерывание, а в прерывании считывается и ADC12MEM1, и ADC12MEM2... они же могут быть еще не заполнены? Нужно было бы по идее писать
if (ADC12IFG & BIT0)
resault[index] = ADC12MEM0;
if (ADC12IFG & BIT1)
resault[index] = ADC12MEM1;... разъясните, пожалуйста.
И вот еще вопрос - index объявляю глобальным, массивы СV1[], CV2[] - тоже. В прерывании веду счетчик(index++), заполняю массивы. В теле проги, если index>=10, ADC12CTL0 &= ENC;
Отправляю сразу по заполнению себе на COM элементы массива - вроде все нормально, заполняются. А после остановки АЦП они пустые, и счетчик 0. В чем тут дело? 05.gif
Подскажите, помогите.
rezident
Цитата(Daria @ Aug 9 2008, 02:28) *
а вот по поводу АЦП:
Мне сказали, что цикл while(ADC12IFG & BIT3); - это некорректное использование процесссора. smile.gif типа, чего он будет столько тиков в пустую ждать.
Верно. Глупый простой получается.
Цитата(Daria @ Aug 9 2008, 02:28) *
Надо работать с прерываниями. и вот вопрос -
Прерывания. как я понимаю, приходят после установки флагов ADC12IFGx, которые устанавливаются после заполнения ADCMEMx. Если режим повторно-последовательный, то прерывания будут после обработки каждого канала?
Количество прерываний зависит от установленных флагов разрешения прерываний в регистре ADC12IE. Если вы используете непрерывный режим преобразования по кольцу, то достаточно установить только один бит, соответствующий последнему каналу. И при возникновении прерывания одним чохом считывать все регистры ADC12MEMx. Одно преобразование ADC12 занимает 13 тактов ADC12CLK. Да плюс еще время сэмплирования каждого канала. Максимальное количество каналов преобразования у ADC12 - 16, время на считывание одного канала не более 5 тактов MCLK, так что если частота MCLK не слишком низкая (у вас вроде 8Мгц была?), то вполне можно успеть считать результаты всех преобразований до того, как обновится результат преобразования ADC самого первого в очереди. Если же не будете успевать (например, MCLK значительно ниже ADC12CLK), то можно два, три бита разрешения прерываний в ADC12IE установить, но с некоторым интерливом. Частоту вызова прерываний надо стараться делать поменьше и время нахождения в прерывания соответственно тоже.
Цитата(Daria @ Aug 9 2008, 02:28) *
И вот еще вопрос - index объявляю глобальным, массивы СV1[], CV2[] - тоже. В прерывании веду счетчик(index++), заполняю массивы. В теле проги, если index>=10, ADC12CTL0 &= ENC;
Отправляю сразу по заполнению себе на COM элементы массива - вроде все нормально, заполняются. А после остановки АЦП они пустые, и счетчик 0. В чем тут дело? 05.gif
Не совсем понял суть проблемы. Видимо опять на код нужно взглянуть. cranky.gif Могу только предположить, что вы неправильно управляете остановом АЦП. Прочитайте внимательно в User's Manual как нужно корректно останавливать преобразование когда используется непрерывный режим преобразования последовательности каналов. Просто сбросить бит ENC в этом случае недостаточно. Останов произойдет, только после завершения всей последовательности преобразований
http://www.gaw.ru/html.cgi/txt/doc/micros/msp430/arh/17.htm
Daria
Цитата(rezident @ Aug 9 2008, 05:10) *
Количество прерываний зависит от установленных флагов разрешения прерываний в регистре ADC12IE. Если вы используете непрерывный режим преобразования по кольцу, то достаточно установить только один бит, соответствующий последнему каналу.

Ага, ясно. Спасибо smile.gif

Цитата(rezident @ Aug 9 2008, 05:10) *
Не совсем понял суть проблемы. Видимо опять на код нужно взглянуть. cranky.gif Могу только предположить, что вы неправильно управляете остановом АЦП. Прочитайте внимательно в User's Manual как нужно корректно останавливать преобразование когда используется непрерывный режим преобразования последовательности каналов. Просто сбросить бит ENC в этом случае недостаточно. Останов произойдет, только после завершения всей последовательности преобразований

да читала, читала, и не один раз. Соображение, видимо, хромает. Как говорил Винни-Пух - "оно хорошее. Только почему-то хромает" biggrin.gif Ну и пусть останов произойдет после завершения последовательности, этого и хочу- последний канал обработал и остановился. А прямо из прерывания остановить нельзя? А то как-то неудобно получается. Хочу считать только десять выборок. если вести счетчик в прерываниях, а потом if index>=10, то реально больше считает, пока проверит, пока остановится. понятно, что если массивы задать жестко [10], то остальные значения по фиг. Но зачем же лишнее время работать. Или тогда уж не повторно-последовательный режим делать, а просто однократный последовательный, и ровно десять раз запускать? тоже как-то странно.
Подскажите! smile.gif
rezident
Цитата(Daria @ Aug 10 2008, 02:42) *
Ну и пусть останов произойдет после завершения последовательности, этого и хочу- последний канал обработал и остановился. А прямо из прерывания остановить нельзя?
Можно. Первой же командой в прерывании установите CONSEQ=0 и ENC=0. Преобразование остановится немедленно.
Цитата(Daria @ Aug 10 2008, 02:42) *
Или тогда уж не повторно-последовательный режим делать, а просто однократный последовательный, и ровно десять раз запускать? тоже как-то странно.
А режим однократной последовательности каналов вас не устраивает? Там правда битом ENC нужно "тогглить" после окончания каждой последовательности.
Daria
Так. Ну вот, как-то не выходит ничего sad.gif
Если в прерывании тормозить
#pragma vector=ADC_VECTOR
__interrupt void ADC12ISR (void)
{
CV1[cindex] = ADC12MEM3;
CV2[cindex] = ADC12MEM4;
ADC12CTL0 &= ENC;
index++;
if (index==9)
{ ADC12CTL1 &= CONSEQ_3;
ADC12CTL0 &= ENC;

}
}
То все равно долго тормозится, несмотря на сброс ENC. успевает прийти еще куча прерываний. Ну и, естественно, если в теле проги сбрасывать, то тоже долго останавливается. Как быть?
И, главное
if (flag==2)
{
ADC12CTL0 |= ADC12SC;

if (index>=9)
{
//АDC12CTL0 &= ENC;
flag=0;
CVx[cindex]=filter(CV1,10);
CVy[cindex]=filter(CV2,10);
index=0;
cindex++;

}
}
if (cindex>3)
cindex=0;
После старта АЦП до того, как успевает прийти 10-е прерывание, условие if (index>=9) уже проверяется, после остановки АЦП выход из последнего прерывания на строке if (cindex>3), потом - к началу проги, попадая сверху-вниз снова на строку if (flag==2), входит в if и запускает АЦП 05.gif То есть в условие if (index>=9) просто никогда не попадает.
Как быть? Подскажите темной непросвещенной женщине, как грамотно это написать biggrin.gif - нужно набирать массив, останавливать АЦП и усреднять данные набранных массивов. Блин, с циклами while все было просто и понятно biggrin.gif
fmdost
Может так?
Код
#pragma vector=ADC_VECTOR
{
if ( ! (index<9))  { //если уже насобирал скока надо
  ADC12CTL1 &= CONSEQ_3; //остановить
  ADC12CTL0 &= ENC;
  }
CV1[cindex] = ADC12MEM3; //последний байт то-же бы надо обработать
CV2[cindex] = ADC12MEM4;
ADC12CTL0 &= ENC;
index++; //станет на один больше, ну и пусть.
}

Что такое cindex++ и почему не в прерывании? Можно его заменить на index++ ?
.
rezident
Все дело в том, что вы маски неверно накладываете. Команда
Код
ADC12CTL1 &= CONSEQ_3;

сбрасывает все биты кроме CONSEQ0 и CONSEQ1, а нужно наоборот - сбросить только эти два бита. Вот так
Код
ADC12CTL1 &= ~CONSEQ_3;

или вот так
Код
ADC12CTL1 &= CONSEQ_3^0xFFFF;

Следовательно ваш обработчик прерывания будет выглядеть так.
Код
#pragma vector=ADC_VECTOR
__interrupt void ADC12ISR (void)
{ CV1[cindex] = ADC12MEM3;
  CV2[cindex] = ADC12MEM4;
  if (index<9)
    index++;
  else    
  { ADC12CTL1 &= ~CONSEQ_3;
    ADC12CTL0 &= ~ENC;
    ADC12IFG = 0;
  }
}

Насчет остального не совсем понятно. Что за index, что за cindex и что за flag? Опишите реализованный вами алгоритм более полно.
Dog Pawlowa
Цитата(Daria @ Aug 11 2008, 23:19) *
Как быть? Подскажите темной непросвещенной женщине, как грамотно это написать biggrin.gif - нужно набирать массив, останавливать АЦП и усреднять данные набранных массивов. Блин, с циклами while все было просто и понятно biggrin.gif


Daria,
(хм, мою внучку так зовут biggrin.gif ),
предлагаю идею алгоритма.

Главное правило - ничего не усложнять, и не делать сложных условий и ожиданий.
У MSP430 быстрый АЦП, хорошо работающий в режиме последовательного измерения всех каналов.
Инициализируется вот так (у меня тактируется от таймера B, в качестве опорного берется напряжение питния):

Код
void ADCconfiguration(void)

//SMCLK = 7372800;  T=0,136 us
//tsample =100 us;  Nsample = 100/0,136 = 735
// SHT1_11 gives 768 cycles

{    ADC12CTL0=    SHT1_11+SHT0_11+ADC12ON; //    Vref off
    ADC12CTL1=    CONSEQ_3        //     repeat sequence of channels
                +ADC12SSEL_3    //     SMCLK
//                +ADC12DIV_7        //
                +SHS_3        //start when TimerB.Out1
                +SHP;        //    
    ADC12MCTL0=    INCH_0+SREF_0;        //    Vr+ = +AVcc, Vr- = AVss
    ADC12MCTL1=    INCH_1+SREF_0;
    ADC12MCTL2=    INCH_2+SREF_0;
    ADC12MCTL3=    INCH_3+SREF_0;
    ADC12MCTL4=    INCH_4+SREF_0;
    ADC12MCTL5=    INCH_5+SREF_0;
    ADC12MCTL6=    INCH_6+SREF_0;
    ADC12MCTL7=    INCH_7+SREF_0+EOS;
    ADC12CTL0|=    ENC;               //;start of ADC
};


После этой инициализации в регистрах ADC12MEM0...ADC12MEM7 результат последнего измерения соответствующего канала. Берите его в любой момент времени и используйте.
Daria
Цитата(Dog Pawlowa @ Aug 12 2008, 10:14) *
После этой инициализации в регистрах ADC12MEM0...ADC12MEM7 результат последнего измерения соответствующего канала. Берите его в любой момент времени и используйте.

Хм. Серьезно? smile.gif Вот просто так и ничего больше не надо? biggrin.gif Надо попробовать, большое спасибо. А прерывания АЦП запретить?
Но вот по поводу прерываний - я переделала прогу и получилось гораздо хуже, чем с циклами while smile.gif
Вот код:
Настройка АЦП(еще раз, на всякий случай)
ADC12CTL0 = ADC12ON + SHT0_8+ MSC;
ADC12CTL1 = CSTARTADD0 + CSTARTADD1 + ADC12SSEL_1+ CONSEQ_1 + SHS_1 + SHP ;
ADC12MCTL3 = INCH_3;
ADC12MCTL4 = INCH_4 + EOS;
ADC12CTL1|= ENC;
После всех настроек
for(;;)
{send_int(azimut);
}
}

#pragma vector=TIMERA0_VECTOR
__interrupt void TimerA0(void)
{ _BIC_SR(GIE);
i++;
if (i >= 2000)

{ i = 0;
P1OUT = 0;
TACCR0 = 0;

ADC12CTL0 |= ENC;
Vs[0] = ADC12MEM3;
Vs[1] = ADC12MEM4;
ADC12CTL0 &= ENC;
TACCR0 = 2000;
}
if (i == 1)
{
TACCR0 = 0;
P1OUT = BIT5;
TACCR0 = 5000;
}

_BIS_SR(GIE);
}

#pragma vector=ADC_VECTOR
__interrupt void ADC12ISR (void)
{_BIC_SR(GIE);
if (P1OUT & BIT5)
{
ADC12CTL0 |= ENC;

while(ADC12IFG&BIT3);
Vr[0]= ADC12MEM3;
while(ADC12IFG&BIT4);
Vr[1] = ADC12MEM4;

ADC12CTL0 &= ~ENC;


offset[0] = (Vs[0] -Vr[0])/2;
offset[1] = (Vs[1] -Vr[1])/2;

Vx = Vr[0] - offset[0];
Vx = Vr[1] - offset[1];

float F[2];
azimut = calculation (Vx,Vy,F);


}

_BIC_SR(GIE);
}
Делается то же. что и всегда smile.gif Вывод в единице, постоянно по переключению таймера снимаются показания с датчика Vr и ведется счетчик прерываний. после 2000-го прерывания вывод переключается в ноль и снимаются показания Vs. Вычисляются смещения и calculation - основное вычисление.
в чем проблема - получается, что постоянно сижу в прерываниях, работает все очень медленно и глючно 07.gif Индексы глобальные. но если попытаться перенести все эти if в цикл for(;;), то ничего не работает. Может оттого, что выход из прерывания происходит в то место, из которого в это прерывание ушли, и проверка условий просто постоянно "проскакивает". sad.gif Короче попытка перенести любой из if в цикл for не работает, а так, как у меня - работает, но ужасно 05.gif Да, наверное не обязательно постоянно дергать битом ENC, но без этого тоже не работает smile.gif беда, да и только smile.gif
Вот то, что Вы, уважаемый Dog Pawlowa, сказали про АЦП - это оч хорошо, проблема только в том, что надо четко отличать, когда преобразование АЦп относится к короткому импульсу(P1OUT=0), а когда rк длинному (P1OUT= BIT5)/
Вот такой огромный вопрос smile.gif
rezident
Daria, извините за откровенность, но у вас какой-то бред получается sad.gif Вы вообще понимаете суть и механизм работы прерываний? Я не знаю вашего возраста и семейного статуса, но надеюсь что "детская" ассоциация разъяснения сути прерываний будет вам понятна. smile.gif
Представте себе, что вы - мама. Сидите на лавочке с подругой и беседуете. Рядом в песочнице ковыряется и бегает возде нее ваш малыш. И вот в произвольный момент времени малыш подбегает к вам и, прерывая ваш разговор с подругой, спрашивает: "мама, а правда, что море мокрое?" "Правда", отвечаете вы. Малыш, удовлетворенный ответом, убегает, а вы возвращаетесь к разговору с подругой. Через некоторое (опять недетерменированное время) малыш снова подбегает к вам и, прерывает разговор, очередным вопросом: "мама, а небо голубое?" "Конечно голубое, сынок. Подними голову и убедись сам", отвечаете вы. Малыш, задрав высоко голову, долго рассматривает проплывающие в голубом небе облака, а вы по-прежнему продолжаете отложенный разговор с подругой.
Такая аналогия вам понятна? wink.gif
Развиваю мысль дальше.
Запрет прерываний. "Сынок, перестань отвлекать маму вопросами. Дай маме с тетей спокойно поговорить."
Разрешение прерываний. "Сынок, у тебя там все нормально? Никто тебя не обижает? Ну если что-то случится сразу беги ко мне и скажи."
Тот исходник, что вы привели можно переложить на детский сюжет так.
Мама, вместо того, чтобы разговаривать с подругой, отпускает малыша в песочницу и внимательно следит, чтобы он не упал и не перепачкался. Как только ребенок падает, она, отряхнув его одежду, говорит: "Ну вот, смотри как ты перепачкался! Не ходи больше в песочницу, поиграй рядом с мамой, пока я с этой тётей побеседую". Усадив ребенка рядом, продолжает беседу.
А эта аналогия понятна?
Вы запускаете преобразование АЦП и, не выходя из прерывания, ждете окончания преобразования и установки бита готовности. Спрашивается ЗАЧЕМ? 07.gif Факт возникновения прерывания УЖЕ является свидетельством о том, что преобразование выполнено и в регистре ADC12MEMx находится готовое значение. После перехода по вектору прерывания вам нужно только считать это значение и при необходимости (пере)запустить или остановить цикл последующих преобразований АЦП. Ждать готовности преобразования, находясь прямо в прерывании нет никакой необходимости!

По структуре всей программы. У вас есть основной цикл, где вы что-то там вычисляете, усредняете и при необходимости передаете наружу. У вас есть таймер, по которому вычисляются временные интервалы и идет управление переключением выходного сигнала и работой АЦП. Работа таймера происходит асинхронно (НЕсинхронно) работе основного цикла. АЦП в принципе тоже работает асинхронно и основному циклу и таймеру, но запуск цикла преобразований АЦП происходит синхронно с таймером. Поэтому запуск АЦП после переключения выходного сигнала должен производится из таймерного прерывания. А останов уже в прерывании АЦП. Обмен между прерываниями и основным циклом можно реализовать посредством семафоров (флагов). В прерывании устанавливается семафор, сигнализирующий. например, о том, что цикл измерений был завершен. В основном цикле этот семафор постоянно опрашивается и как только обнаруживается, что он установлен, результаты измерения обрабатываются, а семафор завершения цикла измерений сбрасывается. Таким образом производится взаимодействие асинхронных процессов. Ну насчет буферов, как средства синхронизации потоков данных для асинхронных процессов, по-моему вы уже знаете.

И еще просьба. Оформляйте, пожалуйста, ваш текст исходников в теги code. Это такая решеточка над полем редактирования сообщения. Так сохраняются все отступы и код получается более удобным для восприятия.
Kurt
2rezident: я восхищен силой вашего воображения ))
_3m
Цитата(Kurt @ Aug 13 2008, 08:41) *
2rezident: я восхищен силой вашего воображения ))

+1
резиденту зачот!
Daria
Цитата(rezident @ Aug 13 2008, 03:30) *

Rezident, циклы while совершенно случайно оказались в прерывании! Просто копировала и ... дело было вечером... 05.gif Так что совершенно зря разразились такой тирадой smile.gif Впрочем, не зря, долго еще могла не заметить. А история про маленького мальчика - супер. biggrin.gif Но - запрещение прерываний в прерывании таймера и АЦП делаю осознанно smile.gif , при переключении вывода должен пройти переходной процесс, прежде чем можно разрешать преобразование АЦП. Так что переключение вывода и старт АЦП должны совершенно точно делаться в разные моменты времени. Такие дела. А вообще - как всегда, спасибо biggrin.gif
rezident
Цитата(Daria @ Aug 13 2008, 23:06) *
Но - запрещение прерываний в прерывании таймера и АЦП делаю осознанно smile.gif , при переключении вывода должен пройти переходной процесс, прежде чем можно разрешать преобразование АЦП.
Если вы еще раз заглянете в User's Guide и внимательно прочитаете главу 2.2.3 Interrupt Processing -> Interrupt Acceptance, то там пункт №6 гласит
Цитата
6) The SR is cleared with the exception of SCG0, which is left unchanged.
This terminates any low-power mode. Because the GIE bit is cleared,
further interrupts are disabled.

Бит GIE находится к статусном регистре SR, который при переходе по вектору прерывания очищается (за исключением бита SCG0). Т.е. переход на программу обработки прерывания запрещает все маскируемые прерывания автоматически. Поэтому дополнительно запрещать все прерывания в обработчике прерывания нет необходимости. Находясь в прерывании можно разрешить прерывания, если вы контролируете размер стека и готовы к ситуации с вложенными прерываниями.
Цитата(Daria @ Aug 13 2008, 23:06) *
Так что переключение вывода и старт АЦП должны совершенно точно делаться в разные моменты времени. Такие дела.
Вы можете запустить преобразование АЦП синхронно с переключением выхода, но выкинуть из расчета результат первого преобразования, используя время, затраченное на это преобразование, как временнУю паузу. Либо использовать этот же таймер (CCR2, например) для генерации временнОй отметки запуска преобразования АЦП. Ожидать в прерывании возможности запуска АЦП имеет смысл только, если требуемая пауза совсем небольшая, не более полусотни тактов MCLK. ИМХО.
Daria
Цитата(rezident @ Aug 13 2008, 22:19) *

Ясно, спасибо
zhevak
Ух-х, какие крутые виражи! Ух, как человека мысли колбасят:
Цитата( @ Aug 13 2008, 23:06) *
... Так что совершенно зря разразились такой тирадой smile.gif

И сразу же, не давая осознать -- ба-бах!
Цитата
Впрочем, не зря, ...


Rezident -- восхищаюсь Вашим терпением. Буду счастлив, если даму удастся выдернуть из девичьего восприятия мира. Да, длинный путь предстоит, долго ей топать. Но, человека надо вытаскивать. Тем более, сам чел этого хочет.

Daria, не останавливайтесь на полпути! Мы все за Вас переживаем и хотим получить полноценного товарища. Глупой Вас никто не считает, просто склад ума/логика мышления у Вас несколько иные. Это не плохо и не хорошо, это -- просто имеет быть.

"Не важно -- кто ты и где ты сейчас. Важно -- куда ты живешь" (С) не помню чье.
Всех благ!
Daria
Цитата(zhevak @ Aug 14 2008, 12:09) *
Rezident -- восхищаюсь Вашим терпением. Буду счастлив, если даму удастся выдернуть из девичьего восприятия мира. Да, длинный путь предстоит, долго ей топать. Но, человека надо вытаскивать. Тем более, сам чел этого хочет.

Я сама восхищаюсь его терпением 07.gif Я бы себя уж давно послала, причем с громкими выкриками biggrin.gif
Цитата(zhevak @ Aug 14 2008, 12:09) *
Daria, не останавливайтесь на полпути!

Ну не знаю, не знаю. мы женщины все такие непоследовательные - то почитать, то пострелять... biggrin.gif
Цитата(zhevak @ Aug 14 2008, 12:09) *
Мы все за Вас переживаем

Надеюсь, аппетит не пропал? wink.gif
Цитата(zhevak @ Aug 14 2008, 12:09) *
Всех благ!

И Вам!! Спасибо за мнение

Rezident, по поводу буфера, пример которого вы приводили. А что, если cbuf.rIdx будет равен cbuf. wIdx +1? Т.е. если считывание "обгонит" запись? Может, стоит поверять if((cbuf.cntr< max_bufsize)&(cbuf.rIdx !=cbuf. wIdx +1))?
Или опять мойс клад ума/логика мышления меня подвел? sad.gif тогда - ой, беда, беда - огорчение.
rezident
Цитата(Daria @ Aug 15 2008, 02:47) *
Rezident, по поводу буфера, пример которого вы приводили. А что, если cbuf.rIdx будет равен cbuf. wIdx +1? Т.е. если считывание "обгонит" запись? Может, стоит поверять if((cbuf.cntr< max_bufsize)&(cbuf.rIdx !=cbuf. wIdx +1))?
Или опять мойс клад ума/логика мышления меня подвел? sad.gif тогда - ой, беда, беда - огорчение.
Вы про циклический буфер? Считывание не может обогнать запись, т.к. перед считыванием должен проверяется счетчик записей. Если счетчик равен нулю, то буфер пуст. А если при этом имеется "разбег" индексов, то его нужно обнулить, установив оба индекса в начало буфера. Только при этом конечно же нужно соблюдать атомарность доступа, чтобы не произошло сбоев записей при одновременном доступе к переменным индексов и счетчика. Атомарность доступа можно обеспечить временным запретом прерываний.
В дополнение. Ситуация когда cbuf.rIdx будет равен cbuf. wIdx +1 не исключена. Но она может быть только в случае, когда весь буфер заполнен. При этом счетчик элементов буфера должен иметь значение равное размеру буфера.
Хотя пожалуй нет. Я ошибся. Счетчик в этом случае будет равен размер_буфера-1. Т.к. для полностью заполненного (и для пустого) циклического буфера значения индексов д.б. равны.
Daria
Ага, будем считать с буфером приема разобрались smile.gif Теперь что касается передачи. Во-первых, все-таки мой старый вопрос - как в этом случае обойтись без этих циклов while, которые ждут установки флага о готовности? ведь тут тоже пустой простой... Что касается буфера, то у меня как-то не получилось biggrin.gif и даже наверное не буду приводить код, опять будете смеяться biggrin.gif Хотя бы принцип верный? -
Запись в буфер идет в прерывании АЦП, считывается постоянно в цикле for(;;); так как вообще говоря передача должна идти посылками по 4 байт (стартовый символ"*", два байта данных (int) и стоповый "#"), то считывается сначала в массив [4], а этот массив передается по байтам через регистр U0TXBUF. Но, когда передается, как обойтись без этих while?
zhevak
О-хо-хо... Ну да ладно!

Даша, я предполагаю, что Вы только-только подходите к понятью принципов работы событийно-управляемых (event-driven) автоматов. Вы уже освоили линейное и процедурное
программирование, теперь перед Вам открывается совершенно другой мир. Другая планета. Здесь все не так.


Извините, маленькая лекция.

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

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

Вот основной цикл нашей программы:
Код
  while (1)
  {
    GetKey();
    DoSomething();
    DisplayResult();
  }


Рассмотрим вызов функция GetKey(). Вроде бы все ясно -- функция должна вернуть код нажатой клавиши. А что делать, если ни одна клавиша не нажата? Значит, функция не вернет управление. Допустим, что это так. Тогда получается, что наше устройство постоянно как-бы зависает на этой функции.

И действительно, посчитать результат и вывести его -- занимает не так уж и много времени по сравнению с тем, что юзер нажимает кнопочки. Юзер вообще может вообще пойти попить кофейку.

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

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

Противоречие в современных устройствах заключается в том, что периферийных устройств много, а "думатель" (АЛУ) -- один.

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

Добавляя в цикл две функции GetCurrentTime() и (), мы не решим проблему.
Код
  while (1)
  {
    GetKey();
    DoSomething();
    DisplayResult();
    GetCurrentTime();
    DisplayTime ();
  }


Понятно, что управление, зависая в GetKey(), будет блокировать всю программу. Понятно, что часы будут подновляться только при нажатии какой-либо кнопочки. Страшно неудобно. Такое изделие никто не купит. Что делать?

Можно пойти двумя путями. (Сразу скажу, Вы, Daria, как мне кажется, в своей программе идете первым путем.)


Путь первый (а-ля-DOS): добавить еще пару функций IsKeyHit() и IsTimeChanged(), которые будут возвращать управление сразу. Такие функции еще называют неблокирующими. Эти функции "говорят" только о том, что нажата ли вообще какая-нибудь клавиша или нет, изменилось ли время или еще нет.

Теперь наш цикл будет выглядеть так:

Код
while (1)
{
  if (IsKeyHit())
  {
     GetKey();
     DoSomething();
     DisplayResult();
  }
  if (IsTimeChanged())
  {
    GetCurrentTime();
    DisplayTime();
  }
}


В этом случае, наша программа будет со скоростью света совершать холостые обороты. Вот смотрите, скорость работы МК составляет миллионы инструкций в секунду, скорость нажатия клавиш -- максимум десяток-другой в секунду, а про именение текущего времени, так вообще раз в секунду. Единственное, что напрягает, -- это кпд программы. Но, с другой стороны, прога-то работает! А это есть самое главное. Ибо ведь никому не нужно устройство, которое не выполняет своей функции. А наше устройство свою функцию выполняет. Ведь так? (Заказчика не интересует, как и на какой парадигме строится программа, Его интересует только конечный результат. А мы его, не смотря на дремучесть нашего кода, обеспечиваем.)

Второй путь (а-ля-Windows): Это то, о чем я говорил в самом начале поста, это событийно-управляемый автомат. Работает эта штука следующим образом.

В таком автомате (системе) есть два дуальных механизма.

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

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

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


Итак, возвращаемся к событийно-управляемым системам. Поставщиками событий в первую очередь являются не что иное, как всевозможные прерывания от периферийных устройств. Во вторую очередь, это могут быть какие-то результаты работы программы. (Я догадываюсь, сейчас это несколько сложно понять. Но что делать? Ведь не всегда бывает легко с первого раза въехать в какие-то новые понятия. Тем более, если эти понятия очень емкие и не вписываются в установившиеся, привычные для нас рамки. Терпите. Учитесь. Другого пути нет.)

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

Еще пару-тройку раз для уяснения:

Произошло перещелкивание секунды в таймере -- возникло прерывание. Заходим, записываем сообщение в очередь, выходим.

Закончил АЦП измерение -- прерывание. Заходим, сообщение в очередь, выходим.
Очередной байт отправлен (по USART-у) -- прерывание. Заходим, сообщение в очередь, выходим.

Думаю, идея понятна?


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

Теперь нам не надо периодически пробегать (говорят – заниматься поллингом) по всем нашим ПУ, опрашивать каждое – есть ли нажатие кнопочки или нет, говов ли АЦП или нет, тикнул ли таймер или нет.

Вместо всего этого, теперь имеется единственная функция GetMessage() и вслед за ней располагается так называемый кракер сообщений (т.е. расшифровщик). Задача этой "парочки" -- вытянуть из очереди сообщение, и вызывать соответствующую функцию по его обработке.

Выглядит это намного приятнее:
Код
  unsigned char msg;

  while ()
  {
    if (GetMessage(&msg))
      switch (msg)
      {
        case MSG_KEYPRESS:
          DoKey();
          break;

        case MSG_TICK:
          DisplayCurrentTime();
          break;
  
        case MSG_ADCREADY:
          SendResult();
          break;

        case MSG_TXD:
          SendNextByte();
          break;

        ...
      }

      // а здесь вот -- самое удобное место пинать собаку (WDR)
  }


Теперь пара слов о переменной msg. У меня здесь это -- просто байт, но в случае очень навороченной системы Вам никто не запрещает для кодирования сообщений вместо байта использовать слово или вообще структуру. Важно только одно: важно соблюсти атамарность операций занесения и извлечения из очереди сообщений. Т.е., другими словами, сделать так, чтобы не получилось, что система, начав записывать одно сообщение в очередь, окажется в состоянии другого прерывания, которое запишет свое сообщение. Потом в очередь будет дозаписан остаток от первого сообщения. В результате в очереди сообщений получится фигня полная. Все рухнет.

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

Не надо также забывать, что обработчики сообщений не должны долго работать, т.е. они не должны узурпировать процессорное время. Я как-то занимался одним проектом. Там нужно было выводить инфу на графический LCD, одновременно нужно было оперативно реагировать на кнопочки и держать связь с компом и дюжиной периферых устройств, подцепленных к двух UART'ам. Сложность состояла в том, что LCD подключался по квадратной (I2C) шине. Длина шлейфа была метра полтора. Я не рискнул, "зажигать" его на 400 кГц. А на 100 кГц, обновление экрана занимало почти четверть (или даже более, не помню точно!) секунды. Если бы обновление LCD было реализовано в одной функции-обработчике, то это легко бы привело к переполнению очереди сообщений. В моменты перерисовки дисплея реакция на клавиатуру отсутствовала бы, связь бы с ПУ нарушалась бы. Приятного мало.

Я сделал так, я разбил LCD на несколько областей, и для перерисовки каждой области создавал свое индивидуальное сообщение. В результате, перерисовка LCD выполнялась как серия перерисовок его отдельных областей. Эта серия сообщений, понятно что, могла быть "разбавлена" сообщениями от клавиатуры и от других ПУ. В целом это создало картину практически мгновенной реакции на нажатие клавиш, способности устройства рисовать на экране и одновременно (и без задержек) заниматься периферией, отвечать компу на его запросы.

Не знаю, что еще можно добавить. С одной стороны, сказать еще много чего можно, а с другой... а то-ли это место (форум), где надо много говорить?

Насчет критики. Я, вобщем-то, как-то не рассчитывал на нее. Просто писал и все. Даже не столько пытался ответить Daria, сколько вообще хотел поделиться своим опытом. Но поскольку я тут много чего сказал, критика должна быть.
rezident
Цитата(Daria @ Aug 17 2008, 17:13) *
Запись в буфер идет в прерывании АЦП, считывается постоянно в цикле for(;;); так как вообще говоря передача должна идти посылками по 4 байт (стартовый символ"*", два байта данных (int) и стоповый "#"), то считывается сначала в массив [4], а этот массив передается по байтам через регистр U0TXBUF. Но, когда передается, как обойтись без этих while?
Дык точно также как вы с АЦП делаете. Использовать передачу по прерываниям.
uart0.h
Код
#ifndef UART0_H
#pragma pack(2)
typedef struct st_uart  //тип структуры данных буфера UART0
{ unsigned int cntr;    //счетчик буфера
  unsigned int idx;     //индекс буфера
  unsigned char buf[10];//буфер UART
} st_uart;
#pragma pack()
#define UART0_H
#endif

main.c
Код
#include "uart0.h"

unsigned int adcRes;//результат преобразования АЦП, который нужно передать
st_uart uart;           //структура данных буфера UART0

void main( void )
{
  while(1)
  {
//  ...
    if ((IE1&UTXIE0)==0); //проверяем готовность UART0 к передаче, бит UTXIE0
    { uart.idx=0;         //используем еще и как признак окончания передачи пакета
      uart.cntr=0;
      uart.buf[uart.cntr++]='*';  //символ начала пакета
      uart.buf[uart.cntr++]=adcRes>>8;  //старший байт результата
      uart.buf[uart.cntr++]=adcRes&0xFF;//младший байт результата
      uart.buf[uart.cntr++]='#';  //символ конца пакета
      IFG1|=UTXIFG0;     //установим флаг, чтобы инициировать прерывания UART0
      IE1|=UTXIE0;        //разрешим прерывания UART0
    }
//  ...
  }
}

#pragma vector=USART0TX_VECTOR
#pragma type_attribute=__interrupt
void UART0TX_ISR(void)
{ if (uart.cntr>0)    //проверяем количество символов в буфере
  { U0TXBUF=uart.buf[uart.idx++]; //передаем текущий символ
    uart.cntr--;      //уменьшаем счетчик количества символов
  }
  else
    IE1&=~UTXIE0;     //если буфер пуст, то сбрасываем бит разреш.прерыв. UART0
}

Я использовал в примере typdef на случай, если вы разбиваете проект на отдельные модули. Если же у вас вся программа в одном файле, то можно объявить структуру uart прямо перед main-ом. Вместо строки
Код
st_uart uart;           //структура данных буфера UART0

Бит разрешения прерывания от передатчика UART0 я использовал как флаг готовности буфера передатчика. Он устанавливается в main, одновременно разрешая прерывания от передатчика и сбрасывается в обработчике прерывания по завершению передачи буфера. Для передачи 4-х байтного пакета будут 5 прерываний от передатчика UART0. В последнем прерывании передачи символа не будет, а будет лишь сброшен UTXIE0.

Цитата(zhevak @ Aug 17 2008, 20:49) *
Интересен вызов функция GetKey(). Вро де бы все ясно -- функция должна вернуть код клавиши. А что делать, если ни одна клавиша не нажата? Получается, функция

управление не должна возвращать. Допустим, что это так. Тогда получается, что наше устройство постоянно как-бы зависает на этой функции. И действительно, посчитать

результат и вывести его, действительно, занимает не так уж и много времени по сравнению с тем, что юзер может вообще пойти попить кофейку. Да?

Но для нашего устройства такое построение программы не страшно. Поскольку предопределенные действия выполняются тогда, когда юзер соизволит нажать кнопочку.
Извините, но у вас ерунда написана. Функция GetKey() никогда не циклится на опросе, а всегда и в любом случае возвращает код нажатой клавиши, иначе она бы называлась не GetKey, а WaitKey biggrin.gif Только в случае когда ни одна клавиша не нажата функций GetKey возвращает скан-код NULL. А если идет запись в буфер клавиатуры, то просто запись туда не производится. Поэтому бесконечный цикл вовсе не зависает на опросе клавиатуры, а крутится себе как и положено wink.gif Естественно, что в функции DoSomething() должна быть предусмотрена обработка скан-кода с кодом NULL.
zhevak
Цитата(rezident @ Aug 17 2008, 21:09) *
Извините, но у вас ерунда написана. Функция GetKey() никогда не циклится на опросе, а всегда и в любом случае возвращает код нажатой клавиши, иначе она бы называлась не GetKey, а WaitKey biggrin.gif Только в случае когда ни одна клавиша не нажата функций GetKey возвращает скан-код NULL. А если идет запись в буфер клавиатуры, то просто запись туда не производится. Поэтому бесконечный цикл вовсе не зависает на опросе клавиатуры, а крутится себе как и положено wink.gif Естественно, что в функции DoSomething() должна быть предусмотрена обработка скан-кода с кодом NULL.


Не, не! Не стоит так сильно реагировать на имя!

Функция GetKey() -- это моя функция, не библиотечная. Про библиотеки не говорю. Более того, я не писал готовый коупи-пэйст код, а излагал парадигму. Жаль что Вы зацепились за эту мелочь. Если Вас это очень смущает, ну замените что-ли название функции на GetPressedKey().
rezident
Цитата(zhevak @ Aug 17 2008, 21:51) *
Не, не! Не стоит так сильно реагировать на имя!

Функция GetKey() -- это моя функция, не библиотечная. Про библиотеки не говорю. Более того, я не писал готовый коупи-пэйст код, а излагал парадигму. Жаль что Вы зацепились за эту мелочь. Если Вас это очень смущает, ну замените что-ли название функции на GetPressedKey().
Да меня вообще не волнует названия функций smile.gif, а "прицепился" я как раз а вашей "парадигме", то бишь к описанию алгоритма "вечного цикла". Я бы еще понял зацикливаение на опросе клавиатуры, если бы перед этим MCU ушел в режим "спячки" для энергосбережения. Вы же описали другой алгоритм, который я не считаю оптимальным в частности и верным в общем. А если вы хотели описать "парадигму" расслоения на os, driver и application, то извините, сделали это не очень внятно. laughing.gif
zhevak
Цитата(rezident @ Aug 17 2008, 21:56) *
Да меня вообще не волнует названия функций smile.gif, а "прицепился" я как раз а вашей "парадигме", то бишь к описанию алгоритма "вечного цикла". Я бы еще понял зацикливаение на опросе клавиатуры, если бы перед этим MCU ушел в режим "спячки" для энергосбережения. Вы же описали другой алгоритм, который я не считаю оптимальным в частности и верным в общем. А если вы хотели описать "парадигму" расслоения на os, driver и application, то извините, сделали это не очень внятно. laughing.gif

не надо laughing.gif -- Я не описывал расслоение. Я описывал только парадигму событийно-управляемой системы. Т.е. ситему, которая работает на принципе реагирования на какие-либо события. Система не занимается поллингом. Система ждет событий, в свободное от работы время совершает холостые циклы. Если хотите -- может вообще спать. Не проблема!

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

Изложенная мною (но не я автор!) парадигма построения программ, предполагает, что она применяется к устройствам, которые намного сложнее, чем простой генератор прямоугольных импульсов. Да, действительно, там проще и легче в основном цикле программы сначала проверять конкретное ПУ (UART, ADC, таймер,...) и, если оно готово, вызывать соответствующую функцию. После чего переходить к проверке следующего ПУ. И т.д. до конца цикла.
Daria
smile.gif Да-а-а 07.gif утром отправляю вопрос, иде себе спокойненько по своим делам, захожу вечером, а тут!... 07.gif Целая переписка Маркса и Энгельса! Я, когда защищалась на своем матфаке, то у меня доп. диплом был по квалификации "преподаватель" smile.gif Этот диплом представлял собой цикл лекций по истории развития теории вероятностей. Там тоже было много всяких примеров, баек и исторических справок. к чему это я? biggrin.gif А к тому, что вам, господа, особенно вам, zhevak, можно не только диплом защищать по специальности "преподаватель программирования контроллеров" , а целую кандидатскую по теме "история развития программирования контроллеров и ваще программирования" biggrin.gif
Но - если чел находит столько времени, сил и желания помочь и рассказать, то - большое спасибо, заходите еще, буду рада и все такое. Rezident, ваша лекция про детей и песочницы тоже была просто класс biggrin.gif a14.gif
Да, rezident, я давно знала, что вы хороший, но вы - ПРОСТО СУПЕР! biggrin.gif Большое спасибо за помощь. Даже не знаю, как вас благодарить за месяцы мучений со мной(а еще сколько предстоит... smile.gif )
На досуге подумаю об этом smile.gif (ну, ну - утихомирьтесь, случайно заглянувшие сюда пошляки) Будут пожелания - высказывайтесь biggrin.gif Есть пара непоняток по вашему коду, но пока не буду спрашивать - вдруг в процессе внедрения сама пойму biggrin.gif
заходите обязательно еще!
Эх, кто бы мне еще по поводу датчиком HMC помог - непонятки все же с калибровкой... т.е. самый простой способ понятен и почти работает, но при таком ТЗ он никак не годится... В разделе "датчики, метрологи и т.п." все на эту тему молчат crying.gif но это так, мысли вслух, и к теме не отностится
Daria
здравствуйте снова. Такой вопрос - во время работы проги, для того, чтобы выполнить калибровку, хочу перенастроить АЦП из однократно-последовательного режима в повторно-последовательный. И для калибровки работать без прерываний. Это ведь делается редко и при выполнении калибровки все остальное вообще не работает, можно же тогда и с циклами while, правильно? smile.gif Дело в том, что все остальное делается теперь по прерываниям и, запрещая их, очень легко и просто все останавливаю. Так вот. Могу я написать так:
ADC12CTL0&= ~ ENC;
ADC12CTL1 &= ~ CONSEQ_1
ADC12CTL1 |= CONSEQ_3
Ну и после калибровки
ADC12CTL0&= ~ ENC;
ADC12CTL1 &= ~ CONSEQ_3
ADC12CTL1 |= CONSEQ_1
Нет тут какого-нибудь недопустимого действия? А то мало ли, чего мое альтернативное мышление натворит smile.gif
rezident
Цитата(Daria @ Aug 19 2008, 23:42) *
можно же тогда и с циклами while, правильно? smile.gif
Можно. Даже нужно. Чтобы не переопределять основной алгоритм измерения в прерывании.
Цитата(Daria @ Aug 19 2008, 23:42) *
Могу я написать так:
ADC12CTL0&= ~ ENC;
ADC12CTL1 &= ~ CONSEQ_1
ADC12CTL1 |= CONSEQ_3
Ну и после калибровки
ADC12CTL0&= ~ ENC;
ADC12CTL1 &= ~ CONSEQ_3
ADC12CTL1 |= CONSEQ_1
Нет тут какого-нибудь недопустимого действия? А то мало ли, чего мое альтернативное мышление натворит smile.gif
Можно, только аккуратнее с битовыми масками. Чистить биты CONSEQx лучше всегда так
Код
ADC12CTL1 &=~CONSEQ_3;

чтобы каждый раз не задумываться и случайно не "пролететь", ошибившись с предыдущим режимом измерения ADC12. Ну и конечно после изменения режима, не забыть вновь разрешить преобразование, установив ENC. А так, все верно, за исключением того, что хорошо бы изменить порядок сброса CONSEQx и ENC. Сначала останов последовательности преобразований сбросом CONSEQx, а потом сброс ENC. Это может быть существенным когда вы используете (разрешили) прерывание ADC12.
Daria
Цитата(rezident @ Aug 20 2008, 01:40) *
А так, все верно, за исключением того, что хорошо бы изменить порядок сброса CONSEQx и ENC. Сначала останов последовательности преобразований сбросом CONSEQx, а потом сброс ENC. Это может быть существенным когда вы используете (разрешили) прерывание ADC12.

Спасибо!
Daria
Здравствуйте. rezident, по поводу примера буфера передачи, который Вы написали smile.gif
для чего делается вот это
#pragma pack(2)
...
#pragma pack()
Как я понимаю, это выравнивание? т.е. #pragma pack(2) - теперь структура будет иметь четное количество байтов, а #pragma pack() - возвращение к установке "по умолчанию"? Так или не совсем? А зачем тогда именно pack(2)? Если написать pack(1) - то будет ровно то количество байт, которое задается, т.е. четыре.
? Объясните чуть подробнее, пожалуйста. smile.gif И еще - почему именно buf[10], чем обусловлено 10 и как его выбирать? smile.gif

И вообще мне не понятно, при таком протоколе, а это вроде довольно распространенный: *, данные, #- как быть при приеме? Вот цифровое значение # - 35, а если приходит такое число? Вот азимут у меня, допустим, 35 - тогда же путаница получится?
Вот у меня такой флажок 0хFF - уникален, учитывая, что передаю по 6 бит smile.gif А как обычно делают?
rezident
Цитата(Daria @ Aug 22 2008, 00:01) *
для чего делается вот это
#pragma pack(2)
...
#pragma pack()
Как я понимаю, это выравнивание?
Да, это выравнивание структуры.
Цитата(Daria @ Aug 22 2008, 00:01) *
т.е. #pragma pack(2) - теперь структура будет иметь четное количество байтов,
Нет, не "иметь четное количество байт", а члены структуры будут выровнены на границу 2-х байтового слова.
Цитата(Daria @ Aug 22 2008, 00:01) *
а #pragma pack() - возвращение к установке "по умолчанию"?
Да.
Цитата(Daria @ Aug 22 2008, 00:01) *
А зачем тогда именно pack(2)?
Потому, что MSP430 16-и разрядный микроконтроллер и доступ к переменным структуры по невыровненному на границу 16-и битного слова указателю может привести к очень неприятным ошибкам.
Цитата(Daria @ Aug 22 2008, 00:01) *
Если написать pack(1) - то будет ровно то количество байт, которое задается, т.е. четыре.
Общее количество байт занимаемое данной конкретной (упоминаемой ранее) структурой будет то же самое, т.к. она уже сама по себе выровнена за счет подбора порядка следования переменных внутри структуры. Но на архитектуре большей разрядности, при другом размере выравнивания размер структуры может увеличиться.
Цитата(Daria @ Aug 22 2008, 00:01) *
? Объясните чуть подробнее, пожалуйста. smile.gif
Объясняю. Имеем вот такую структуру, выровненную на размер 1 байта
Код
#pragma pack(1)
typedef struct qqq_t
{ char a;
  int b;
  long c;
  char d;
} qqq_t;
#pragma pack(1)

qqq_t QQQ;

qqq_t *pQ=&QQQ;

Допустим, что компилятор расположил структуру, начиная с адреса 0x0200, выровняв ее на границу байта. Тогда структура займет в памяти 8 байт.
QQQ.a расположена по адресу 0x0200 - нормально, 1 байт
QQQ.b расположена по адресу 0x0201 - внимание! слово по нечетному адресу!!!, 2 байта
QQQ.c расположена по адресу 0x0203 - опять внимание!!!, 4 байта
QQQ.d расположена по адресу 0x0207 - допустимо, т.к. переменная 1-но байтовая, 1 байт
Теперь, если мы захотим изменить значение, например, элемента b, то в указатель будет загружен ее адрес 0x0201.
Код
pQ->b += 1;

И мы получим полный облом - обращение к 16-и битному слову по нечетному адресу!!! Но поскольку архитектура MSP430 такого не позволяет, то этим оператором будет считано значение слова по четному адресу (0x0200, вместо 0x0201) и увеличенное на единицу значение будет записано по этому же адресу (0x0200). В результате изменится не только значение переменной под именем b, но и значение переменной a тоже.
Если же мы предусмотрительно сделаем выравнивание на границу 16-и разрядного слова, то структура расположится в памяти, заняв правда уже 9 байт, так.
QQQ.a по адресу 0x0200 - нормально, 1 байт
неиспользуемый 1 байт по адресу 0x0201
QQQ.b по адресу 0x0202 - нормально, 2 байт
QQQ.c по адресу 0x0204 - нормально, 4 байт
QQQ.d по адресу 0x0208 - нормально, 1 байт
Но зато при этом выравнивании не будет никаких проблем при обращении с помощью указателя к любому члену структуры, т.к. все адреса членов структур четные и выравнены на границу 16-и разрядного слова.
В случаях когда мы сами себе хозяева, а состав структуры данных нам не "спущен сверху", то можно перетасовать элементы структуры так, чтобы они располагались в памяти более оптимально, без "дырок", образующихся при выравнивании. Лучше располагать их в порядке убывания размеров переменных. Например.
Код
#pragma pack(2)
typedef struct qqq_t
{ long c;
  int b;
  char a;
  char d;
} qqq_t;
#pragma pack()

Еще момент. Вроде бы тут структура уже самоупорядочилась и выравнивание вроде не требуется. Но это только для того случая, когда структура сама по себе единица данных. Если же мы попробуем включить эту структуру в состав другой (невыравненной) структуры, то можем получить снова такую же ситуацию, которая описана чуть выше. Так что я за то, чтобы структуры всегда были выровнены. Причем если используется 32-х битная архитектура, то выравнивать лучше на границу 32-х битного слова (на 4 байта).
Вообще говоря, стандартом Си гарантируется только порядок следования членов структуры, а насчет занимаемого в памяти размера структуры стандарт ничего не говорит. Это отдается на откуп программисту и компилятору. Поэтому при переносе структур данных на архитектуры разной разрядности нужно быть очень внимательным.
Цитата(Daria @ Aug 22 2008, 00:01) *
И еще - почему именно buf[10], чем обусловлено 10 и как его выбирать? smile.gif
А данном случае 10 это абстрактное число взятое мной "с потолка" smile.gif Размер буфера выбирается, исходя из максимального размера пакета или, исходя из времени определяемого неравномерностью доступа к буферу. Первый случай рассматривать по-моему не стоит. Размер буфера д.б. не меньше, чем максимальный размера принимаемого пакета. Это для случая когда требуется принять весь пакет целиком и только потом начать его разбор.
Второй случай. Допустим, нам хочется организовать непрерывный поток данных через UART и для этого мы хотим использовать буфер аля FIFO - линейный буфер по принципу "первый вошел-первый вышел". Пускай отправка 1 символа UART занимает условно 10мс. А темп поступления данных на конвейер UART определяется какими-либо внешними событиями, распределенными по времени неравномерно от 0,2 мс до 20мс. Какого размера буфер требуется? Исходя из самого худшего предположения, за время отправки 1 символа (10мс) мы можем получить до 50 символов с периодом 0,2мс. Значит размер буфера должен быть (10-0,2)/0,2>=49 элементов. Конечно, это для случая нормального (гауссового) распределения задержек в указанном диапазоне. В противном случае нам может не хватить даже буфера из 1000 элементов, если на каком-то весьма продолжительном отрезке времени данные будет поступать с высоким темпом (с минимальной задержкой).
Цитата(Daria @ Aug 22 2008, 00:01) *
И вообще мне не понятно, при таком протоколе, а это вроде довольно распространенный: *, данные, #- как быть при приеме? Вот цифровое значение # - 35, а если приходит такое число? Вот азимут у меня, допустим, 35 - тогда же путаница получится?
Вот у меня такой флажок 0хFF - уникален, учитывая, что передаю по 6 бит smile.gif А как обычно делают?
Обычно символы начала (и конца) пакета являются уникальными. При этом либо весь пакет как-то обрабатывается (кодируется) так, чтобы не допустить символ с кодом начала (конца) пакета, либо совпадающие по коду символы дублируются, либо заменяются на последовательность уникальных символов. Посмотрите, например, описание протокола Wake, там эта проблема решена вполне изящно.
Daria
Как всегда - большое спасибо! smile.gif про выравнивание ясно, про размер буфера - тоже(примерно).
С протоколом буду разбираться, будут вопросы - завтра задам biggrin.gif
Daria
Здравствуйте снова.
Вот опять вопросы_
Делаю усреднение по по тридцать отсчетов
Код
int filter1(int input[], int in)
{    float result = 0;                  
    int i;
    if (in>0)
    {
        for (i = 0; i < 30; i++)    
            result += input[i];
            result=result/in;
        for (i = 0; i < 30; i++)    
            input[i] = input[i+1];    

        return (int)(result);
    }
}

Потом в прерывании АЦП
Код
if((index>=29)|(f==1))//если уже набрался массив  из 30 элементов для усреднения
    {               f=1;
        Vr[0][29]= ADC12MEM3;
        Vr[1][29] = ADC12MEM4;
                    
        ADC12CTL0 &= ~ENC;    
                
        buf[0] = filter1(Vr[0], 30);    //фильтр
        buf[1] = filter1(Vr[1], 30);
                    
        offset[0] = (Vs[0] -buf[0])/2;     // вычисляются смещения на основе Vset, Vreset
        offset[1] = (Vs[1] - buf[1])/2;
        Vx = buf[0] - offset[0];        //координаты вектора маг. индукции
        Vy = buf[1] - offset[1];
                        
        index=0;            
                        
                    
            float F[2];
            azimut = calculation (Vx,Vy,F);
            send_int(azimut);
    }
                
if ((index<29)&(f==0))// пока массив не набрался
                
               {
                   Vr[0][index]= ADC12MEM3;
                   Vr[1][index] = ADC12MEM4;
        index++;
        ADC12CTL0 &= ~ENC;    
               }

Массив Vr[2][30] глобальный. Преобразование АЦП разрешается в прерываниях от таймера, которые происходят через 5мс. флаг f становится равным единице, когда уже набрался массив, индекс потом обнуляется, чтобы не возрастал постоянно до максимального значения, а флаг остается.
Кривовато?
Дело в том, что почему-то для 30 отсчетов работает, для 35 - тоже, а для 40 - уже нет, передача данных не идет. Хотя в отладчике все вроде происходит нормально, хоть до ста отсчетов давай.
И все-таки по поводу буфера,rezident.
Выравнивание #pragma pack(2) почему-то дает какой-то жуткий эффект - данные перепутываются и вообще не идут. А если по одному байту выравнивать, то нормально...но и данные ведь по одному байту пишутся. И вообще-то, зачем делать счетчик буфера int? Буфер же однобайтовый... 05.gif
Поясните еще, я все же видимо, непонятливая 05.gif
rezident
Цитата(Daria @ Aug 30 2008, 00:53) *
Делаю усреднение по по тридцать отсчетов

Что-то не совсем понятно, что за сдвиг вы формируете при усреднении? Вы пытаетесь фильтровать скользящим средним что ли? Или просто накапливаете 30 значений, а потом среднее арифметическое берете? Тогда зачем сдвиг элементов буфера? cranky.gif
Цитата(Daria @ Aug 30 2008, 00:53) *
Потом в прерывании АЦП
Вычисления и фильтрация в прерывании??? 07.gif Эх, Дарья, ничево вы видимо не поняли sad.gif
Цитата(Daria @ Aug 30 2008, 00:53) *
if((index>=29)|(f==1))//если уже набрался массив из 30 элементов для усреднения
Сразу же ошибка. Оператор | это "побитовое ИЛИ". Более уместное в логическом выражении "логическое ИЛИ" записывается как ||.
Цитата(Daria @ Aug 30 2008, 00:53) *
...

Поскипано. Мне грустно видеть как жуткие вычисления с плавучкой в прерывании транжирят драгоценное время crying.gif
Цитата(Daria @ Aug 30 2008, 00:53) *
if ((index<29)&(f==0))// пока массив не набрался
Опять аналогичная ошибка. "Логическое И" записывается как &&. Одиночный амперсанд & это "побитовое И".
Цитата(Daria @ Aug 30 2008, 00:53) *
Кривовато?
Не то слово!
Цитата(Daria @ Aug 30 2008, 00:53) *
Дело в том, что почему-то для 30 отсчетов работает, для 35 - тоже, а для 40 - уже нет, передача данных не идет. Хотя в отладчике все вроде происходит нормально, хоть до ста отсчетов давай.
А вы в отладчике посмотрите сколько циклов занимает ваш расчет и фильтрация wink.gif Поделите на тактовую MCLK и выясните, как оно соотносится с 5мс таймерными прерываниями? Ведь пока вы в прерывании АЦП считаете, все остальные прерывания запрещены.
Цитата(Daria @ Aug 30 2008, 00:53) *
И все-таки по поводу буфера,rezident.
Выравнивание #pragma pack(2) почему-то дает какой-то жуткий эффект - данные перепутываются и вообще не идут. А если по одному байту выравнивать, то нормально...но и данные ведь по одному байту пишутся.
Что перепутывается? Кто куда не идет? Про какой именно буфер речь? Если про буфер UART, то что там не так с выравниванием? Вынесите сам буфер (массив) типа unsigned char наружу структуры, как самостоятельный глобальный массив переменных. Его-то выравнивать не нужно. А в структуре только указатель на начало этого массива оставьте.
Цитата(Daria @ Aug 30 2008, 00:53) *
И вообще-то, зачем делать счетчик буфера int? Буфер же однобайтовый... 05.gif
Потому, что в Си тип int является машинно-ориентированно-оптимальным. Поскольку MSP430 16-и разрядный микроконтроллер, то 16-и битовое слово является для него оптимальным. А, например, на ARM тип int 32-х битный. Нет, вы конечно же можете имеете право и unsigned char для счетчика использовать, если стоит задача экономии ОЗУ. У MSP430 есть команды для работы с байтовыми операндами. Я же совершенно не против laughing.gif
Daria
Да знаю я, знаю, что долго сидеть в прерывании нехорошо, не ругайтесь так... biggrin.gif Но тут какая проблема была - если вычисления делать просто в цикле for(;;), то за время, пока эти вычисления делаются, уже успевает прийти несколько прерываний, и данные обновляются прямо по ходу вычислений. И происходит такая вещь - если плавно поворачивать плату, то оцифрованый уровень меняется далеко не плавно, жутко скачет во время поворота и долго "успокаивается", если вращать быстро, то будет долгий переходной процесс - т.е. улетает сначала намного выше реального уровня, затем медленно возвращается. Вот, по-женски так объясняю, самой смешно biggrin.gif я думаю, это происходит оттого, что допустим координата Х уже просчиталась, а У еще нет, пришло прерывание, данные обновились, У считается, и уже характеризует новое положение, а Х - еще старое, поэтому при вращении возникают большие ошибки.
Такое дело нежелательно, потому что компас может медленно вращаться во время использования, или сразу после остановки может быть обращение к нему. Я перенесла вычисления прямо в прерывание, и все стало меняться просто на заглядение плавно biggrin.gif Кстати, на ваш вопрос - да, усредняю скользящим, среднее арифметическое - это глупо даже для меня biggrin.gif Каждый раз дожидаться, пока наберется 30 отсчетов... Я жду, пока они наберутся только в самом начале работы, а потом каждое новое число записывается 30-м элементом массива, и массив усредняется и сдвигается.
Ну да, зато теперь тратится куча времени... 05.gif
А, если все же не в прерывании считать, то как бы избавится от той проблемы, о которой я говорю? если, конечно, вы меня поняли biggrin.gif у меня в последнее время с объяснением не лады 05.gif
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.