|
MSP430 - снова вопросы от чайника, Вопросы от чайника про MSP и магнитный компас |
|
|
|
Jul 23 2008, 19:17
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
 Здравствуйте. Кто читал предыдущую мою тему "MSP430 - вопросы от чайника", тот поймет - это снова я  Тему закрыли, слишком долго висела. А вопрсов все больше. Если кому-то вдруг окажется не влом разбираться, то Вот кусок проги: 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; } В ЧЕМ ПРОБЛЕМА: Перед выполнением калибровки присылаю элементы массива, полученного при считывании показаний с выводов, - оказывается, что в массив записываются по два-три раза одно значение, хотя этого не может быть. Соответственно, из-за этого не работает и все остальное Может, неправильно настроен АЦП? Или в чем может быть дело? И, если кому-то вдруг не влом разбираться - функции записи во flash и чтения flash - можно ли сделать как-то поумнее?
|
|
|
|
17 страниц
1 2 3 > »
|
 |
Ответов
(1 - 99)
|
Jul 23 2008, 22:03
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Некоторое (продолжительное) время не заглядывал в форум, а когда заглянул оказалось, что вашу тему прикрыли. По поводу Flash. Проверять готовность к записи во Flash (бит BUSY) при выполнении программы записи прямо из Flash нет необходимости. Вот такая вот продвинутая технология у MSP430  Почему так, объяснять еще раз не буду. Читайте внимательно 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), а вы работаете с ними как Бог на душу положит.  Стропальщик убил бы просто такого крановщика, если бы остался жив после его несанкционированных действий.  Эта иллюстрация синхронных действий. Для иллюстрации же асинхронных процессов к картине работы башенного крана нужно добавить несколько КАМАЗов с кирпичем, которые приезжают на стройку асинхронно работе крана, когда у них получится, и сваливают кирпич в общую кучу. Которую потом стропальщик с крановщиком общими синхронными действиями перемещают на строящийся уровень дома  Эта моя пространная тирада относится в частности к строке Цитата ADC12CTL0 |=ADC12SC + ENC; CVx[cindex] = ADC12MEM3; CVy[cindex] = ADC12MEM4; Где вы запускаете преобразование АЦП и тут же, не дожидаясь окончания выполнения преобразования, считываете результаты оцифровки непонятно чего. Раз вы не используете прерывание от АЦП, ну подождите для приличия установки флага готовности что ли Код 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 ...
|
|
|
|
|
Jul 24 2008, 19:29
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Ага, все же заглянули  спасибо за комментарии и критику, все подробно и пространно. По поводу АЦП я уже сама доперла, установку флагов жду, режим использую последовательный, и бит MSC ставлю  Теперь работает. По поводу read_flash - ну да, но это так, для понта отдельная функция А по поводу write_flash - хм. но она работает. я проверяла, во flash пишется не фигня  может это мне просто везет? Про буфер - спасибо, попробую. Вообще спасибо, что не забыли
|
|
|
|
|
Jul 25 2008, 03:03
|
Участник

Группа: Участник
Сообщений: 54
Регистрация: 25-09-07
Пользователь №: 30 836

|
Цитата(Daria @ Jul 24 2008, 23:29)  А по поводу write_flash - хм. но она работает. я проверяла, во flash пишется не фигня  может это мне просто везет? Пока везет. В данном случае строка: *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)что два раза подряд вызовет стирание сегмента, причем без пауз между ними. В лучшем случае это вызовет удвоенный износ флешки, в худшем - неизвестно (поскольку до подобных действий наверняка еще никто не додумался  )
|
|
|
|
|
Jul 25 2008, 18:27
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(=DS= @ Jul 25 2008, 07:03)  *addr = 0;транслируется в MOV.W #0x0, 0(R13) MOV.W #0x0, 2(R13)что два раза подряд вызовет стирание сегмента, причем без пауз между ними. В лучшем случае это вызовет удвоенный износ флешки, в худшем - неизвестно (поскольку до подобных действий наверняка еще никто не додумался  )  а как тогда написать? Такая строка была в примере, который я скачала с ИАРовского сайта, - "фиктивная запись". Как тогда написать фиктивную запись ? И вот еще ВОПРОС- понимаю, что, наверное, глупый, но - Когда гружу прогу с помощью IAR Kickstart - работает нормально, считает четко все - я проверяла. Но так как код уже превышает 4кБ, гружу с помощью полной версии - на COM приходит полная ересь. С чем это может быть связано? Код такой же - один в один. Я думала, может дело в доступе к библиотекам, но не ругается же на код, на #include <math.h>, работает, считает, пишет... только фигню Вот. Не ругайтесь! - уж такая я глупая Вот сама функция, она принимает два массива и на их основании вычисляет и пишет во 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( ) - решение системы методом Гаусса, она точно работает, не буду приводить - к тому же вряд ли кому-то будет охота в ней разбираться  Такие вопросы
Сообщение отредактировал Daria - Jul 25 2008, 18:31
|
|
|
|
|
Jul 25 2008, 19:47
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Jul 26 2008, 00:27)   а как тогда написать? Такая строка была в примере, который я скачала с ИАРовского сайта, - "фиктивная запись". Как тогда написать фиктивную запись ?  Конечно фиктивная запись, вот только в примере скорее всего был указатель другого типа (unsigned int или unsigned char). Как поступить в данной ситуации я вам уже выше подсказал - используйте явное приведение типа. Раз у вас указатель на float, то при фиктивной записи преобразуйте его к указателю на unsigned int. Код *((unsigned int *)addr)=0; Цитата(Daria @ Jul 26 2008, 00:27)  И вот еще ВОПРОС- понимаю, что, наверное, глупый, но - Когда гружу прогу с помощью IAR Kickstart - работает нормально, считает четко все - я проверяла. Но так как код уже превышает 4кБ, гружу с помощью полной версии - на COM приходит полная ересь. С чем это может быть связано? Код такой же - один в один. Версии IAR одинаковы? Настройки проекта одинаковы? И вообще, спрашивается зачем использовать KickStart при наличии полной версии?  Если вы не пользуетесь дебаггером, то для загрузки прошивки в кристалл использовать IAR вообще не обязательно. Пользуйтесь специальными программами. MSPFET Programmer от Kurt-а или FET-Pro430 Lite от Elprotronic или вот тут же в форуме zltigo свою утилиту AT430 предлагает. А вообще правильность работы алгоритма программы можно проверить в симуляторе. Тем более, если у вас есть в наличии результаты работы алгоритма по известным данным.
|
|
|
|
|
Jul 25 2008, 21:05
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
И версии, и настройки. А кикстарт - потому что сначала был только он  FET-Pro430 Lite у меня есть. но не очень-то удобно компилироваться, а потом грузить через нее - проще и быстрее же все делать в ИАРе Дебаггером, кстати, пользуюсь, но проверить алгоритм не получается - он не показывает текущие значения моих переменных, только регистры контроллера  - то же касается симулятора Кстати, если настройка АЦП 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; так нормально? Или опять грузовики с кирпичами понаехали?
|
|
|
|
|
Jul 25 2008, 21:38
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Jul 26 2008, 03:05)  И версии, и настройки. А кикстарт - потому что сначала был только он  FET-Pro430 Lite у меня есть. но не очень-то удобно компилироваться, а потом грузить через нее - проще и быстрее же все делать в ИАРе  Не проще. C-Cpy грузит "дебажную" версию прошивки, а в реальное устройство вы будете компилировать другую версию, без дебаговских примочек. Не нравится FET-Pro430, используйте программатор Курта. Её (программу) кстати можно интегрировать в IAR, т.к. в ней имеется поддержка командной строки. Tools->Configure Tools->New обзываете ее и заполняете необходимые поля параметрами запуска программы MSPFET Programmer. Только для нее нужно использовать Release-ную настройку с форматом выходного файла TI-TXT или HEX. Цитата(Daria @ Jul 26 2008, 03:05)  Дебаггером, кстати, пользуюсь, но проверить алгоритм не получается - он не показывает текущие значения моих переменных, только регистры контроллера  - то же касается симулятора Ну здрасте! А ручками в окно 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; так нормально? Или опять грузовики с кирпичами понаехали?  А зачем вы включаете опору, если ее не использует е при измерении? А так... сойдет  Только я бы перед запуском преобразования флаги ADC12IFG почистил бы на всякий пожарный. Опс! Тока сейчас заметил, неверно все-таки. Нужно так. while((ADC12IFG & BIT3)==0); Пока флаг не установлен, нужно ждать. А у вас инверсное условие. В моем примере выше я тоже ошибся, но поправить уже не могу.
|
|
|
|
|
Jul 25 2008, 21:40
|
Участник

Группа: Участник
Сообщений: 54
Регистрация: 25-09-07
Пользователь №: 30 836

|
Цитата(Daria @ Jul 26 2008, 01:05)  Дебаггером, кстати, пользуюсь, но проверить алгоритм не получается - он не показывает текущие значения моих переменных, только регистры контроллера  - то же касается симулятора Галочка в project->options->linker->output "debug information for C-Spy" стоит?
|
|
|
|
|
Jul 26 2008, 12:38
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Jul 26 2008, 01:38)  Ну здрасте! А ручками в окно View->Watch имя интересуемых переменных или структур забить лень что ли? Или окно View->Locals открыть и наблюдать за автоматическими переменными. rezident, ну вы меня уж за совсем-пресовсем тупую держите!  Конечно, не лень! Но состояние переменных почему-то не отображается, вместо этого error стоит. Только состояние регистров. Я думала, это так всегда Цитата(rezident @ Jul 26 2008, 01:38)  Опс! Тока сейчас заметил, неверно все-таки. Нужно так. while((ADC12IFG & BIT3)==0); Пока флаг не установлен, нужно ждать. А у вас инверсное условие. В моем примере выше я тоже ошибся, но поправить уже не могу.  не, это я тоже ошиблась, когда вам писала - на самом деле я, конечно, while(!(ADC12IFG & BIT3)) пишу. Ага? Тож на тож ведь. или все же не очень корректно? Цитата(=DS= @ Jul 26 2008, 01:40)  Галочка в project->options->linker->output "debug information for C-Spy" стоит? А вот не знаю. Посмотрю, спасибо
|
|
|
|
|
Jul 26 2008, 12:58
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Jul 26 2008, 18:38)  rezident, ну вы меня уж за совсем-пресовсем тупую держите!  Ну зачем же сразу такие эпитеты? Я просто делаю скидку на женскую альтернативную логику. Извините, если обидел. Цитата(Daria @ Jul 26 2008, 18:38)  Но состояние переменных почему-то не отображается, вместо этого error стоит. Только состояние регистров. Я думала, это так всегда  =DS=, указал вероятную причину. Цитата(Daria @ Jul 26 2008, 18:38)  не, это я тоже ошиблась, когда вам писала - на самом деле я, конечно, while(!(ADC12IFG & BIT3)) пишу. Ага? Тож на тож ведь. или все же не очень корректно?  Да пишите себе на здоровье, раз вам такой стиль нравится! Просто я лично при проверке условий предпочитаю явное сравнение с нулем. Для меня так нагляднее и понятнее. Но в свою "религию" я никого насильно не обращаю
|
|
|
|
|
Jul 28 2008, 20:14
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Так вот. Галочка стоит. А состояние переменных все равно error По поводу ошибок в программе - у меня, кстати, все-таки разные версии, Kickstart 5.0, a Evolution 4.0.  Но разве это может повлиять.? Ведь не ругается, все считает... Что-то не так с #include <math.h>? А, еще - они стоят вместе. Могут, перепуться библиотеки? Теперь такие вопросы: rezident, вы вот давно обещали помочь разобраться с таймером  Так, как я это делаю(по переключению флага ) - не пойдет  "Камазы с кирпичами", да и только  При большой скорости пересчета, в принципе, можно забить на некоторые неточности, но, когда нужно выполнять что-то четко по команде... Вы говорили, что можно использовать выход таймера. Как?  Ведь мне не нужно постоянно переключать вывод, нужно долго держать его в единице, а потом короткое время в ноле. Еще- если я запущу еще и таймер В - это не будет излишней перегрузкой программы? Дело в том, что мне надо постоянно фиксировать состояние переменной azimut и где-то раз в секунду смотреть, не изменилась ли она, если изменилась - отсылать команду на COM. Что, если делать это по таймеру В? Короче - помогите, спасите
|
|
|
|
|
Jul 28 2008, 23:05
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Jul 29 2008, 02:14)  Так вот. Галочка стоит. А состояние переменных все равно error  Ну я не знаю.  Попробовал я в своем текущем проекте убирать "птички" в опциях проекта везде, где только встретил "Generate debug ...". В режиме отладки пропала возможность "шагать" по Сишному исходнику. Только по ASM-овому тексту теперь шагает. Но значения глобальных переменных в окне Watch и локальных переменных в окне Locals все равно показывает. Может вы пытаетесь в окне Watch значение локальной переменной посмотреть? Тогда понятно почему ошибку выдает. Цитата(Daria @ Jul 29 2008, 02:14)  По поводу ошибок в программе - у меня, кстати, все-таки разные версии, Kickstart 5.0, a Evolution 4.0.  Но разве это может повлиять.? Ведь не ругается, все считает... Что-то не так с #include <math.h>? А, еще - они стоят вместе. Могут, перепуться библиотеки? В один каталог крайне не рекомендуется устанавливать разные версии одного продукта. Если только с одинаковой версией IDE, но для разных целевых платформ (например, EWAVR и EW430). Устанавливайте версии KickStart и Evalution в разные каталоги, тогда путаницы не будет. Цитата(Daria @ Jul 29 2008, 02:14)  rezident, вы вот давно обещали помочь разобраться с таймером  Так, как я это делаю(по переключению флага ) - не пойдет  "Камазы с кирпичами", да и только  При большой скорости пересчета, в принципе, можно забить на некоторые неточности, но, когда нужно выполнять что-то четко по команде... Вы говорили, что можно использовать выход таймера. Как?  Ведь мне не нужно постоянно переключать вывод, нужно долго держать его в единице, а потом короткое время в ноле. У таймеров 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.  Если еще непонятно, то пояснения будут уже завтра. Кстати, не помню, давал ли я ссылку на русскоязычный переводной вариант 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мс получаю целое число миллисекунд.
|
|
|
|
|
Jul 30 2008, 19:31
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Jul 29 2008, 03:05)  Ну я не знаю.  Попробовал я в своем текущем проекте убирать "птички" в опциях проекта везде, где только встретил "Generate debug ...". В режиме отладки пропала возможность "шагать" по Сишному исходнику. Только по ASM-овому тексту теперь шагает. Но значения глобальных переменных в окне Watch и локальных переменных в окне Locals все равно показывает. Может вы пытаетесь в окне Watch значение локальной переменной посмотреть? Тогда понятно почему ошибку выдает. Ничего подобного  Все же не совсем, не совсем я даунито, я уже говорила. Локальные переменные смотрю, где надо Об остальном - rezident, какой же вы все-таки хороший!  Так все подробно и хорошо. Та функция, которую хотела сделать, вроде работает, а про таймеры... В принципе я пыталась сделать нечто подобное, но возникли проблемы  Какие именно, напишу чуть позже, когда еще раз попробую переделать, руководствуясь вашими комментариями  Спасибо, конечно, огромное. буду разбираться
|
|
|
|
|
Aug 7 2008, 20:43
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Здравствуйте, снова я Цитата(rezident @ Jul 29 2008, 03:05)  Например, если для TA1 выбрать режим выходного модуля "сброс/установка", то по совпадению TAR и CCR1 вывод TA1 сброситься в лог.0, а по совпадению TAR и CCR0 установиться в лог.1. ...Я предлагал вам использовать TimerA в режим счета Continious (с переполением). Выходной модуль TA0 или TA1 использовать для генерации выходного управляющего импульса, который одновременно запускает преобразование АЦП в непрерывном режиме последовательности каналов. Вам нужно только в прерывании от CCR0 или CCR1 вести счетчик и при достижении предпоследнего интервала отсчета импульса перепрограммировать режимы выходного блока и вовремя остановить АЦП для нового запуска по другому фронту TA0 или TA1. Все же я какая-то непонятливая  допустим, режим Continious и режим "сброс/установка", по совпадению TAR и CCR1 вывод TA1 сброситься в лог.0, а по совпадению TAR и CCR0 установиться в лог.1. Но максимальное число. которое я могу записать в CCR0 - FFFF. А мне нужно держать лог. 1 долго. Как быть? Может, установить сначала режим "установка", в прерываниях от CCR0 вести счетчик, а в последнем переключить режим на "сброс". В следующем за ним прерывании опять "установка". Можно так сделать? Вы об этом говорите? Да, а по поводу версий IAR - дело было просто в глючности самой ломаной версии 3.2
|
|
|
|
|
Aug 8 2008, 20:28
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Aug 8 2008, 05:29)  Можно оставить все как у вас сейчас уже работает. И запуск АЦП делать прямо в таймерном прерывании. Сейчас не очень хорошо. Понадобилось вот стыковаться, а у меня с синхронностью выявились большие проблемы  Надо, чтобы все было четко-пречетко. И без камазов с кирпичами  То есть, можно сделать так, как я говорю? а вот по поводу АЦП: Мне сказали, что цикл while(ADC12IFG & BIT3); - это некорректное использование процесссора.  типа, чего он будет столько тиков в пустую ждать. Надо работать с прерываниями. и вот вопрос - Прерывания. как я понимаю, приходят после установки флагов ADC12IFGx, которые устанавливаются после заполнения ADCMEMx. Если режим повторно-последовательный, то прерывания будут после обработки каждого канала? А вот в найденном примере в прерывании пишется static unsigned int index; resault[index] = ADC12MEM0; resault[index] = ADC12MEM1; resault[index] = ADC12MEM2; index=(index+1)%8; как же так?  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. В чем тут дело? Подскажите, помогите.
|
|
|
|
|
Aug 9 2008, 01:10
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Aug 9 2008, 02:28)  а вот по поводу АЦП: Мне сказали, что цикл while(ADC12IFG & BIT3); - это некорректное использование процесссора.  типа, чего он будет столько тиков в пустую ждать. Верно. Глупый простой получается. Цитата(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. В чем тут дело?  Не совсем понял суть проблемы. Видимо опять на код нужно взглянуть.  Могу только предположить, что вы неправильно управляете остановом АЦП. Прочитайте внимательно в User's Manual как нужно корректно останавливать преобразование когда используется непрерывный режим преобразования последовательности каналов. Просто сбросить бит ENC в этом случае недостаточно. Останов произойдет, только после завершения всей последовательности преобразований http://www.gaw.ru/html.cgi/txt/doc/micros/msp430/arh/17.htm
|
|
|
|
|
Aug 9 2008, 20:42
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Aug 9 2008, 05:10)  Количество прерываний зависит от установленных флагов разрешения прерываний в регистре ADC12IE. Если вы используете непрерывный режим преобразования по кольцу, то достаточно установить только один бит, соответствующий последнему каналу. Ага, ясно. Спасибо Цитата(rezident @ Aug 9 2008, 05:10)  Не совсем понял суть проблемы. Видимо опять на код нужно взглянуть.  Могу только предположить, что вы неправильно управляете остановом АЦП. Прочитайте внимательно в User's Manual как нужно корректно останавливать преобразование когда используется непрерывный режим преобразования последовательности каналов. Просто сбросить бит ENC в этом случае недостаточно. Останов произойдет, только после завершения всей последовательности преобразований да читала, читала, и не один раз. Соображение, видимо, хромает. Как говорил Винни-Пух - "оно хорошее. Только почему-то хромает"  Ну и пусть останов произойдет после завершения последовательности, этого и хочу- последний канал обработал и остановился. А прямо из прерывания остановить нельзя? А то как-то неудобно получается. Хочу считать только десять выборок. если вести счетчик в прерываниях, а потом if index>=10, то реально больше считает, пока проверит, пока остановится. понятно, что если массивы задать жестко [10], то остальные значения по фиг. Но зачем же лишнее время работать. Или тогда уж не повторно-последовательный режим делать, а просто однократный последовательный, и ровно десять раз запускать? тоже как-то странно. Подскажите!
|
|
|
|
|
Aug 9 2008, 22:10
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Aug 10 2008, 02:42)  Ну и пусть останов произойдет после завершения последовательности, этого и хочу- последний канал обработал и остановился. А прямо из прерывания остановить нельзя? Можно. Первой же командой в прерывании установите CONSEQ=0 и ENC=0. Преобразование остановится немедленно. Цитата(Daria @ Aug 10 2008, 02:42)  Или тогда уж не повторно-последовательный режим делать, а просто однократный последовательный, и ровно десять раз запускать? тоже как-то странно. А режим однократной последовательности каналов вас не устраивает? Там правда битом ENC нужно "тогглить" после окончания каждой последовательности.
|
|
|
|
|
Aug 11 2008, 20:19
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Так. Ну вот, как-то не выходит ничего Если в прерывании тормозить #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 и запускает АЦП  То есть в условие if (index>=9) просто никогда не попадает. Как быть? Подскажите темной непросвещенной женщине, как грамотно это написать  - нужно набирать массив, останавливать АЦП и усреднять данные набранных массивов. Блин, с циклами while все было просто и понятно
|
|
|
|
|
Aug 11 2008, 21:52
|

Местный
  
Группа: Свой
Сообщений: 479
Регистрация: 8-05-07
Из: г. Ставрополь. Северный Кавказ. Россия
Пользователь №: 27 606

|
Может так? Код #pragma vector=ADC_VECTOR { if ( ! (index<9)) { //если уже насобирал скока надо ADC12CTL1 &= CONSEQ_3; //остановить ADC12CTL0 &= ENC; } CV1[cindex] = ADC12MEM3; //последний байт то-же бы надо обработать CV2[cindex] = ADC12MEM4; ADC12CTL0 &= ENC; index++; //станет на один больше, ну и пусть. } Что такое cindex++ и почему не в прерывании? Можно его заменить на index++ ? .
|
|
|
|
|
Aug 11 2008, 23:19
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Все дело в том, что вы маски неверно накладываете. Команда Код 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? Опишите реализованный вами алгоритм более полно.
|
|
|
|
|
Aug 12 2008, 06:14
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(Daria @ Aug 11 2008, 23:19)  Как быть? Подскажите темной непросвещенной женщине, как грамотно это написать  - нужно набирать массив, останавливать АЦП и усреднять данные набранных массивов. Блин, с циклами while все было просто и понятно  Daria, (хм, мою внучку так зовут  ), предлагаю идею алгоритма. Главное правило - ничего не усложнять, и не делать сложных условий и ожиданий. У 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 результат последнего измерения соответствующего канала. Берите его в любой момент времени и используйте.
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Aug 12 2008, 19:55
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(Dog Pawlowa @ Aug 12 2008, 10:14)  После этой инициализации в регистрах ADC12MEM0...ADC12MEM7 результат последнего измерения соответствующего канала. Берите его в любой момент времени и используйте. Хм. Серьезно?  Вот просто так и ничего больше не надо?  Надо попробовать, большое спасибо. А прерывания АЦП запретить? Но вот по поводу прерываний - я переделала прогу и получилось гораздо хуже, чем с циклами while Вот код: Настройка АЦП(еще раз, на всякий случай) 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); } Делается то же. что и всегда  Вывод в единице, постоянно по переключению таймера снимаются показания с датчика Vr и ведется счетчик прерываний. после 2000-го прерывания вывод переключается в ноль и снимаются показания Vs. Вычисляются смещения и calculation - основное вычисление. в чем проблема - получается, что постоянно сижу в прерываниях, работает все очень медленно и глючно  Индексы глобальные. но если попытаться перенести все эти if в цикл for(;;), то ничего не работает. Может оттого, что выход из прерывания происходит в то место, из которого в это прерывание ушли, и проверка условий просто постоянно "проскакивает".  Короче попытка перенести любой из if в цикл for не работает, а так, как у меня - работает, но ужасно  Да, наверное не обязательно постоянно дергать битом ENC, но без этого тоже не работает  беда, да и только Вот то, что Вы, уважаемый Dog Pawlowa, сказали про АЦП - это оч хорошо, проблема только в том, что надо четко отличать, когда преобразование АЦп относится к короткому импульсу(P1OUT=0), а когда rк длинному (P1OUT= BIT5)/ Вот такой огромный вопрос
|
|
|
|
|
Aug 12 2008, 23:30
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Daria, извините за откровенность, но у вас какой-то бред получается  Вы вообще понимаете суть и механизм работы прерываний? Я не знаю вашего возраста и семейного статуса, но надеюсь что "детская" ассоциация разъяснения сути прерываний будет вам понятна. Представте себе, что вы - мама. Сидите на лавочке с подругой и беседуете. Рядом в песочнице ковыряется и бегает возде нее ваш малыш. И вот в произвольный момент времени малыш подбегает к вам и, прерывая ваш разговор с подругой, спрашивает: "мама, а правда, что море мокрое?" "Правда", отвечаете вы. Малыш, удовлетворенный ответом, убегает, а вы возвращаетесь к разговору с подругой. Через некоторое (опять недетерменированное время) малыш снова подбегает к вам и, прерывает разговор, очередным вопросом: "мама, а небо голубое?" "Конечно голубое, сынок. Подними голову и убедись сам", отвечаете вы. Малыш, задрав высоко голову, долго рассматривает проплывающие в голубом небе облака, а вы по-прежнему продолжаете отложенный разговор с подругой. Такая аналогия вам понятна?  Развиваю мысль дальше. Запрет прерываний. "Сынок, перестань отвлекать маму вопросами. Дай маме с тетей спокойно поговорить." Разрешение прерываний. "Сынок, у тебя там все нормально? Никто тебя не обижает? Ну если что-то случится сразу беги ко мне и скажи." Тот исходник, что вы привели можно переложить на детский сюжет так. Мама, вместо того, чтобы разговаривать с подругой, отпускает малыша в песочницу и внимательно следит, чтобы он не упал и не перепачкался. Как только ребенок падает, она, отряхнув его одежду, говорит: "Ну вот, смотри как ты перепачкался! Не ходи больше в песочницу, поиграй рядом с мамой, пока я с этой тётей побеседую". Усадив ребенка рядом, продолжает беседу. А эта аналогия понятна? Вы запускаете преобразование АЦП и, не выходя из прерывания, ждете окончания преобразования и установки бита готовности. Спрашивается ЗАЧЕМ?  Факт возникновения прерывания УЖЕ является свидетельством о том, что преобразование выполнено и в регистре ADC12MEMx находится готовое значение. После перехода по вектору прерывания вам нужно только считать это значение и при необходимости (пере)запустить или остановить цикл последующих преобразований АЦП. Ждать готовности преобразования, находясь прямо в прерывании нет никакой необходимости! По структуре всей программы. У вас есть основной цикл, где вы что-то там вычисляете, усредняете и при необходимости передаете наружу. У вас есть таймер, по которому вычисляются временные интервалы и идет управление переключением выходного сигнала и работой АЦП. Работа таймера происходит асинхронно (НЕсинхронно) работе основного цикла. АЦП в принципе тоже работает асинхронно и основному циклу и таймеру, но запуск цикла преобразований АЦП происходит синхронно с таймером. Поэтому запуск АЦП после переключения выходного сигнала должен производится из таймерного прерывания. А останов уже в прерывании АЦП. Обмен между прерываниями и основным циклом можно реализовать посредством семафоров (флагов). В прерывании устанавливается семафор, сигнализирующий. например, о том, что цикл измерений был завершен. В основном цикле этот семафор постоянно опрашивается и как только обнаруживается, что он установлен, результаты измерения обрабатываются, а семафор завершения цикла измерений сбрасывается. Таким образом производится взаимодействие асинхронных процессов. Ну насчет буферов, как средства синхронизации потоков данных для асинхронных процессов, по-моему вы уже знаете. И еще просьба. Оформляйте, пожалуйста, ваш текст исходников в теги code. Это такая решеточка  над полем редактирования сообщения. Так сохраняются все отступы и код получается более удобным для восприятия.
|
|
|
|
|
Aug 13 2008, 04:41
|
Участник

Группа: Свой
Сообщений: 63
Регистрация: 16-06-04
Из: Россия, Уфа
Пользователь №: 31

|
2rezident: я восхищен силой вашего воображения ))
|
|
|
|
|
Aug 13 2008, 06:34
|
Знающий
   
Группа: Участник
Сообщений: 745
Регистрация: 28-12-06
Пользователь №: 23 960

|
Цитата(Kurt @ Aug 13 2008, 08:41)  2rezident: я восхищен силой вашего воображения )) +1 резиденту зачот!
|
|
|
|
|
Aug 13 2008, 17:06
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Aug 13 2008, 03:30)  Rezident, циклы while совершенно случайно оказались в прерывании! Просто копировала и ... дело было вечером...  Так что совершенно зря разразились такой тирадой  Впрочем, не зря, долго еще могла не заметить. А история про маленького мальчика - супер.  Но - запрещение прерываний в прерывании таймера и АЦП делаю осознанно  , при переключении вывода должен пройти переходной процесс, прежде чем можно разрешать преобразование АЦП. Так что переключение вывода и старт АЦП должны совершенно точно делаться в разные моменты времени. Такие дела. А вообще - как всегда, спасибо
|
|
|
|
|
Aug 13 2008, 18:19
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Aug 13 2008, 23:06)  Но - запрещение прерываний в прерывании таймера и АЦП делаю осознанно  , при переключении вывода должен пройти переходной процесс, прежде чем можно разрешать преобразование АЦП. Если вы еще раз заглянете в 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. ИМХО.
|
|
|
|
|
Aug 13 2008, 19:59
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Aug 13 2008, 22:19)  Ясно, спасибо
|
|
|
|
|
Aug 14 2008, 08:09
|

Знающий
   
Группа: Свой
Сообщений: 723
Регистрация: 29-08-05
Из: Березовский
Пользователь №: 8 065

|
Ух-х, какие крутые виражи! Ух, как человека мысли колбасят: Цитата( @ Aug 13 2008, 23:06)  ... Так что совершенно зря разразились такой тирадой  И сразу же, не давая осознать -- ба-бах! Цитата Впрочем, не зря, ... Rezident -- восхищаюсь Вашим терпением. Буду счастлив, если даму удастся выдернуть из девичьего восприятия мира. Да, длинный путь предстоит, долго ей топать. Но, человека надо вытаскивать. Тем более, сам чел этого хочет. Daria, не останавливайтесь на полпути! Мы все за Вас переживаем и хотим получить полноценного товарища. Глупой Вас никто не считает, просто склад ума/логика мышления у Вас несколько иные. Это не плохо и не хорошо, это -- просто имеет быть. "Не важно -- кто ты и где ты сейчас. Важно -- куда ты живешь" (С) не помню чье. Всех благ!
--------------------
Хочешь рассмешить Бога -- расскажи ему о своих планах!
|
|
|
|
|
Aug 14 2008, 20:47
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(zhevak @ Aug 14 2008, 12:09)  Rezident -- восхищаюсь Вашим терпением. Буду счастлив, если даму удастся выдернуть из девичьего восприятия мира. Да, длинный путь предстоит, долго ей топать. Но, человека надо вытаскивать. Тем более, сам чел этого хочет. Я сама восхищаюсь его терпением  Я бы себя уж давно послала, причем с громкими выкриками Цитата(zhevak @ Aug 14 2008, 12:09)  Daria, не останавливайтесь на полпути! Ну не знаю, не знаю. мы женщины все такие непоследовательные - то почитать, то пострелять... Цитата(zhevak @ Aug 14 2008, 12:09)  Мы все за Вас переживаем Надеюсь, аппетит не пропал? Цитата(zhevak @ Aug 14 2008, 12:09)  Всех благ! И Вам!! Спасибо за мнение Rezident, по поводу буфера, пример которого вы приводили. А что, если cbuf.rIdx будет равен cbuf. wIdx +1? Т.е. если считывание "обгонит" запись? Может, стоит поверять if((cbuf.cntr< max_bufsize)&(cbuf.rIdx !=cbuf. wIdx +1))? Или опять мойс клад ума/логика мышления меня подвел?  тогда - ой, беда, беда - огорчение.
|
|
|
|
|
Aug 14 2008, 21:01
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Aug 15 2008, 02:47)  Rezident, по поводу буфера, пример которого вы приводили. А что, если cbuf.rIdx будет равен cbuf. wIdx +1? Т.е. если считывание "обгонит" запись? Может, стоит поверять if((cbuf.cntr< max_bufsize)&(cbuf.rIdx !=cbuf. wIdx +1))? Или опять мойс клад ума/логика мышления меня подвел?  тогда - ой, беда, беда - огорчение. Вы про циклический буфер? Считывание не может обогнать запись, т.к. перед считыванием должен проверяется счетчик записей. Если счетчик равен нулю, то буфер пуст. А если при этом имеется "разбег" индексов, то его нужно обнулить, установив оба индекса в начало буфера. Только при этом конечно же нужно соблюдать атомарность доступа, чтобы не произошло сбоев записей при одновременном доступе к переменным индексов и счетчика. Атомарность доступа можно обеспечить временным запретом прерываний. В дополнение. Ситуация когда cbuf.rIdx будет равен cbuf. wIdx +1 не исключена. Но она может быть только в случае, когда весь буфер заполнен. При этом счетчик элементов буфера должен иметь значение равное размеру буфера. Хотя пожалуй нет. Я ошибся. Счетчик в этом случае будет равен размер_буфера-1. Т.к. для полностью заполненного (и для пустого) циклического буфера значения индексов д.б. равны.
|
|
|
|
|
Aug 17 2008, 11:13
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Ага, будем считать с буфером приема разобрались  Теперь что касается передачи. Во-первых, все-таки мой старый вопрос - как в этом случае обойтись без этих циклов while, которые ждут установки флага о готовности? ведь тут тоже пустой простой... Что касается буфера, то у меня как-то не получилось  и даже наверное не буду приводить код, опять будете смеяться  Хотя бы принцип верный? - Запись в буфер идет в прерывании АЦП, считывается постоянно в цикле for(;;); так как вообще говоря передача должна идти посылками по 4 байт (стартовый символ"*", два байта данных (int) и стоповый "#"), то считывается сначала в массив [4], а этот массив передается по байтам через регистр U0TXBUF. Но, когда передается, как обойтись без этих while?
|
|
|
|
|
Aug 17 2008, 14:49
|

Знающий
   
Группа: Свой
Сообщений: 723
Регистрация: 29-08-05
Из: Березовский
Пользователь №: 8 065

|
О-хо-хо... Ну да ладно! Даша, я предполагаю, что Вы только-только подходите к понятью принципов работы событийно-управляемых (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, сколько вообще хотел поделиться своим опытом. Но поскольку я тут много чего сказал, критика должна быть.
Сообщение отредактировал zhevak - Aug 17 2008, 15:42
--------------------
Хочешь рассмешить Бога -- расскажи ему о своих планах!
|
|
|
|
|
Aug 17 2008, 15:09
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(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  Только в случае когда ни одна клавиша не нажата функций GetKey возвращает скан-код NULL. А если идет запись в буфер клавиатуры, то просто запись туда не производится. Поэтому бесконечный цикл вовсе не зависает на опросе клавиатуры, а крутится себе как и положено  Естественно, что в функции DoSomething() должна быть предусмотрена обработка скан-кода с кодом NULL.
|
|
|
|
|
Aug 17 2008, 15:51
|

Знающий
   
Группа: Свой
Сообщений: 723
Регистрация: 29-08-05
Из: Березовский
Пользователь №: 8 065

|
Цитата(rezident @ Aug 17 2008, 21:09)  Извините, но у вас ерунда написана. Функция GetKey() никогда не циклится на опросе, а всегда и в любом случае возвращает код нажатой клавиши, иначе она бы называлась не GetKey, а WaitKey  Только в случае когда ни одна клавиша не нажата функций GetKey возвращает скан-код NULL. А если идет запись в буфер клавиатуры, то просто запись туда не производится. Поэтому бесконечный цикл вовсе не зависает на опросе клавиатуры, а крутится себе как и положено  Естественно, что в функции DoSomething() должна быть предусмотрена обработка скан-кода с кодом NULL. Не, не! Не стоит так сильно реагировать на имя! Функция GetKey() -- это моя функция, не библиотечная. Про библиотеки не говорю. Более того, я не писал готовый коупи-пэйст код, а излагал парадигму. Жаль что Вы зацепились за эту мелочь. Если Вас это очень смущает, ну замените что-ли название функции на GetPressedKey().
--------------------
Хочешь рассмешить Бога -- расскажи ему о своих планах!
|
|
|
|
|
Aug 17 2008, 15:56
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

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

Знающий
   
Группа: Свой
Сообщений: 723
Регистрация: 29-08-05
Из: Березовский
Пользователь №: 8 065

|
Цитата(rezident @ Aug 17 2008, 21:56)  Да меня вообще не волнует названия функций  , а "прицепился" я как раз а вашей "парадигме", то бишь к описанию алгоритма "вечного цикла". Я бы еще понял зацикливаение на опросе клавиатуры, если бы перед этим MCU ушел в режим "спячки" для энергосбережения. Вы же описали другой алгоритм, который я не считаю оптимальным в частности и верным в общем. А если вы хотели описать "парадигму" расслоения на os, driver и application, то извините, сделали это не очень внятно.  не надо  -- Я не описывал расслоение. Я описывал только парадигму событийно-управляемой системы. Т.е. ситему, которая работает на принципе реагирования на какие-либо события. Система не занимается поллингом. Система ждет событий, в свободное от работы время совершает холостые циклы. Если хотите -- может вообще спать. Не проблема! Если Вас не устраивает энергопотребление, то в цикле после кракера и после того, как пнете собаку, поставьте команду для спячки. Проц должен проснутся либо по какому-либо событию, либо по таймеру. Использование таймера (для системного времени), я думаю, в таких системах должно быть предусмотрено. Само собой разумеется, что тайм-аут собаки должен превышать тайм-аут таймера. Изложенная мною (но не я автор!) парадигма построения программ, предполагает, что она применяется к устройствам, которые намного сложнее, чем простой генератор прямоугольных импульсов. Да, действительно, там проще и легче в основном цикле программы сначала проверять конкретное ПУ (UART, ADC, таймер,...) и, если оно готово, вызывать соответствующую функцию. После чего переходить к проверке следующего ПУ. И т.д. до конца цикла.
--------------------
Хочешь рассмешить Бога -- расскажи ему о своих планах!
|
|
|
|
|
Aug 17 2008, 20:49
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
 Да-а-а  утром отправляю вопрос, иде себе спокойненько по своим делам, захожу вечером, а тут!...  Целая переписка Маркса и Энгельса! Я, когда защищалась на своем матфаке, то у меня доп. диплом был по квалификации "преподаватель"  Этот диплом представлял собой цикл лекций по истории развития теории вероятностей. Там тоже было много всяких примеров, баек и исторических справок. к чему это я?  А к тому, что вам, господа, особенно вам, zhevak, можно не только диплом защищать по специальности "преподаватель программирования контроллеров" , а целую кандидатскую по теме "история развития программирования контроллеров и ваще программирования" Но - если чел находит столько времени, сил и желания помочь и рассказать, то - большое спасибо, заходите еще, буду рада и все такое. Rezident, ваша лекция про детей и песочницы тоже была просто класс Да, rezident, я давно знала, что вы хороший, но вы - ПРОСТО СУПЕР!  Большое спасибо за помощь. Даже не знаю, как вас благодарить за месяцы мучений со мной(а еще сколько предстоит...  ) На досуге подумаю об этом  (ну, ну - утихомирьтесь, случайно заглянувшие сюда пошляки) Будут пожелания - высказывайтесь  Есть пара непоняток по вашему коду, но пока не буду спрашивать - вдруг в процессе внедрения сама пойму заходите обязательно еще! Эх, кто бы мне еще по поводу датчиком HMC помог - непонятки все же с калибровкой... т.е. самый простой способ понятен и почти работает, но при таком ТЗ он никак не годится... В разделе "датчики, метрологи и т.п." все на эту тему молчат  но это так, мысли вслух, и к теме не отностится
|
|
|
|
|
Aug 19 2008, 17:42
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
здравствуйте снова. Такой вопрос - во время работы проги, для того, чтобы выполнить калибровку, хочу перенастроить АЦП из однократно-последовательного режима в повторно-последовательный. И для калибровки работать без прерываний. Это ведь делается редко и при выполнении калибровки все остальное вообще не работает, можно же тогда и с циклами while, правильно?  Дело в том, что все остальное делается теперь по прерываниям и, запрещая их, очень легко и просто все останавливаю. Так вот. Могу я написать так: ADC12CTL0&= ~ ENC; ADC12CTL1 &= ~ CONSEQ_1 ADC12CTL1 |= CONSEQ_3 Ну и после калибровки ADC12CTL0&= ~ ENC; ADC12CTL1 &= ~ CONSEQ_3 ADC12CTL1 |= CONSEQ_1 Нет тут какого-нибудь недопустимого действия? А то мало ли, чего мое альтернативное мышление натворит
|
|
|
|
|
Aug 19 2008, 21:40
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Aug 19 2008, 23:42)  можно же тогда и с циклами while, правильно?  Можно. Даже нужно. Чтобы не переопределять основной алгоритм измерения в прерывании. Цитата(Daria @ Aug 19 2008, 23:42)  Могу я написать так: ADC12CTL0&= ~ ENC; ADC12CTL1 &= ~ CONSEQ_1 ADC12CTL1 |= CONSEQ_3 Ну и после калибровки ADC12CTL0&= ~ ENC; ADC12CTL1 &= ~ CONSEQ_3 ADC12CTL1 |= CONSEQ_1 Нет тут какого-нибудь недопустимого действия? А то мало ли, чего мое альтернативное мышление натворит  Можно, только аккуратнее с битовыми масками. Чистить биты CONSEQx лучше всегда так Код ADC12CTL1 &=~CONSEQ_3; чтобы каждый раз не задумываться и случайно не "пролететь", ошибившись с предыдущим режимом измерения ADC12. Ну и конечно после изменения режима, не забыть вновь разрешить преобразование, установив ENC. А так, все верно, за исключением того, что хорошо бы изменить порядок сброса CONSEQx и ENC. Сначала останов последовательности преобразований сбросом CONSEQx, а потом сброс ENC. Это может быть существенным когда вы используете (разрешили) прерывание ADC12.
|
|
|
|
|
Aug 20 2008, 18:20
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Aug 20 2008, 01:40)  А так, все верно, за исключением того, что хорошо бы изменить порядок сброса CONSEQx и ENC. Сначала останов последовательности преобразований сбросом CONSEQx, а потом сброс ENC. Это может быть существенным когда вы используете (разрешили) прерывание ADC12. Спасибо!
|
|
|
|
|
Aug 21 2008, 18:01
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Здравствуйте. rezident, по поводу примера буфера передачи, который Вы написали для чего делается вот это #pragma pack(2) ... #pragma pack() Как я понимаю, это выравнивание? т.е. #pragma pack(2) - теперь структура будет иметь четное количество байтов, а #pragma pack() - возвращение к установке "по умолчанию"? Так или не совсем? А зачем тогда именно pack(2)? Если написать pack(1) - то будет ровно то количество байт, которое задается, т.е. четыре. ? Объясните чуть подробнее, пожалуйста.  И еще - почему именно buf[10], чем обусловлено 10 и как его выбирать? И вообще мне не понятно, при таком протоколе, а это вроде довольно распространенный: *, данные, #- как быть при приеме? Вот цифровое значение # - 35, а если приходит такое число? Вот азимут у меня, допустим, 35 - тогда же путаница получится? Вот у меня такой флажок 0хFF - уникален, учитывая, что передаю по 6 бит  А как обычно делают?
|
|
|
|
|
Aug 21 2008, 21:19
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(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)  ? Объясните чуть подробнее, пожалуйста.  Объясняю. Имеем вот такую структуру, выровненную на размер 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 и как его выбирать?  А данном случае 10 это абстрактное число взятое мной "с потолка"  Размер буфера выбирается, исходя из максимального размера пакета или, исходя из времени определяемого неравномерностью доступа к буферу. Первый случай рассматривать по-моему не стоит. Размер буфера д.б. не меньше, чем максимальный размера принимаемого пакета. Это для случая когда требуется принять весь пакет целиком и только потом начать его разбор. Второй случай. Допустим, нам хочется организовать непрерывный поток данных через 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 бит  А как обычно делают? Обычно символы начала (и конца) пакета являются уникальными. При этом либо весь пакет как-то обрабатывается (кодируется) так, чтобы не допустить символ с кодом начала (конца) пакета, либо совпадающие по коду символы дублируются, либо заменяются на последовательность уникальных символов. Посмотрите, например, описание протокола Wake, там эта проблема решена вполне изящно.
|
|
|
|
|
Aug 29 2008, 18:53
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Здравствуйте снова. Вот опять вопросы_ Делаю усреднение по по тридцать отсчетов Код 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? Буфер же однобайтовый... Поясните еще, я все же видимо, непонятливая
|
|
|
|
|
Aug 29 2008, 20:16
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Aug 30 2008, 00:53)  Делаю усреднение по по тридцать отсчетов Что-то не совсем понятно, что за сдвиг вы формируете при усреднении? Вы пытаетесь фильтровать скользящим средним что ли? Или просто накапливаете 30 значений, а потом среднее арифметическое берете? Тогда зачем сдвиг элементов буфера? Цитата(Daria @ Aug 30 2008, 00:53)  Потом в прерывании АЦП Вычисления и фильтрация в прерывании???  Эх, Дарья, ничево вы видимо не поняли Цитата(Daria @ Aug 30 2008, 00:53)  if((index>=29)|(f==1))//если уже набрался массив из 30 элементов для усреднения Сразу же ошибка. Оператор | это "побитовое ИЛИ". Более уместное в логическом выражении "логическое ИЛИ" записывается как ||. Цитата(Daria @ Aug 30 2008, 00:53)  ... Поскипано. Мне грустно видеть как жуткие вычисления с плавучкой в прерывании транжирят драгоценное время Цитата(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 - уже нет, передача данных не идет. Хотя в отладчике все вроде происходит нормально, хоть до ста отсчетов давай. А вы в отладчике посмотрите сколько циклов занимает ваш расчет и фильтрация  Поделите на тактовую MCLK и выясните, как оно соотносится с 5мс таймерными прерываниями? Ведь пока вы в прерывании АЦП считаете, все остальные прерывания запрещены. Цитата(Daria @ Aug 30 2008, 00:53)  И все-таки по поводу буфера,rezident. Выравнивание #pragma pack(2) почему-то дает какой-то жуткий эффект - данные перепутываются и вообще не идут. А если по одному байту выравнивать, то нормально...но и данные ведь по одному байту пишутся. Что перепутывается? Кто куда не идет? Про какой именно буфер речь? Если про буфер UART, то что там не так с выравниванием? Вынесите сам буфер (массив) типа unsigned char наружу структуры, как самостоятельный глобальный массив переменных. Его-то выравнивать не нужно. А в структуре только указатель на начало этого массива оставьте. Цитата(Daria @ Aug 30 2008, 00:53)  И вообще-то, зачем делать счетчик буфера int? Буфер же однобайтовый...  Потому, что в Си тип int является машинно-ориентированно-оптимальным. Поскольку MSP430 16-и разрядный микроконтроллер, то 16-и битовое слово является для него оптимальным. А, например, на ARM тип int 32-х битный. Нет, вы конечно же можете имеете право и unsigned char для счетчика использовать, если стоит задача экономии ОЗУ. У MSP430 есть команды для работы с байтовыми операндами. Я же совершенно не против
|
|
|
|
|
Aug 30 2008, 18:33
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Да знаю я, знаю, что долго сидеть в прерывании нехорошо, не ругайтесь так...  Но тут какая проблема была - если вычисления делать просто в цикле for(;;), то за время, пока эти вычисления делаются, уже успевает прийти несколько прерываний, и данные обновляются прямо по ходу вычислений. И происходит такая вещь - если плавно поворачивать плату, то оцифрованый уровень меняется далеко не плавно, жутко скачет во время поворота и долго "успокаивается", если вращать быстро, то будет долгий переходной процесс - т.е. улетает сначала намного выше реального уровня, затем медленно возвращается. Вот, по-женски так объясняю, самой смешно  я думаю, это происходит оттого, что допустим координата Х уже просчиталась, а У еще нет, пришло прерывание, данные обновились, У считается, и уже характеризует новое положение, а Х - еще старое, поэтому при вращении возникают большие ошибки. Такое дело нежелательно, потому что компас может медленно вращаться во время использования, или сразу после остановки может быть обращение к нему. Я перенесла вычисления прямо в прерывание, и все стало меняться просто на заглядение плавно  Кстати, на ваш вопрос - да, усредняю скользящим, среднее арифметическое - это глупо даже для меня  Каждый раз дожидаться, пока наберется 30 отсчетов... Я жду, пока они наберутся только в самом начале работы, а потом каждое новое число записывается 30-м элементом массива, и массив усредняется и сдвигается. Ну да, зато теперь тратится куча времени... А, если все же не в прерывании считать, то как бы избавится от той проблемы, о которой я говорю? если, конечно, вы меня поняли  у меня в последнее время с объяснением не лады
|
|
|
|
|
Aug 30 2008, 21:04
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Aug 31 2008, 00:33)  Да знаю я, знаю, что долго сидеть в прерывании нехорошо, не ругайтесь так...  Но тут какая проблема была - если вычисления делать просто в цикле for(;;), то за время, пока эти вычисления делаются, уже успевает прийти несколько прерываний, и данные обновляются прямо по ходу вычислений. Есть два способа решения этой проблемы. Либо на время вычислений в основном цикле запрещать прерывания от АЦП (либо останавливать его) - все равно АЦП не работает, когда вы считаете в прерывании. Либо использовать два циклических буфера. В то время пока вы обсчитываете результаты одного буфера, в другой буфер (по прерываниям АЦП) идет запись новых результатов изменений. В основном цикле вы только устанавливаете номер буфера и флаг сигнализирующий обработчику прерывания АЦП о смене номера буфера. В прерывании АЦП сначала анализируется состояние флага и при необходимости изменяется указатель на буфер, а запись новых значений происходит уже в другой буфер. Цитата(Daria @ Aug 31 2008, 00:33)  И происходит такая вещь - если плавно поворачивать плату, то оцифрованый уровень меняется далеко не плавно, жутко скачет во время поворота и долго "успокаивается", если вращать быстро, то будет долгий переходной процесс - т.е. улетает сначала намного выше реального уровня, затем медленно возвращается. Вот, по-женски так объясняю, самой смешно  я думаю, это происходит оттого, что допустим координата Х уже просчиталась, а У еще нет, пришло прерывание, данные обновились, У считается, и уже характеризует новое положение, а Х - еще старое, поэтому при вращении возникают большие ошибки. Значит у вас где-то ошибка в алгоритме. нужно синхронизировать обновления значений переменных координат так, чтобы не было разбега, как он у вас описан. Цитата(Daria @ Aug 31 2008, 00:33)  Кстати, на ваш вопрос - да, усредняю скользящим, среднее арифметическое - это глупо даже для меня  Каждый раз дожидаться, пока наберется 30 отсчетов... Я жду, пока они наберутся только в самом начале работы, а потом каждое новое число записывается 30-м элементом массива, и массив усредняется и сдвигается. Ну да, зато теперь тратится куча времени...  А вам приниципально усреднять уже готовые значения координат или можно усреднять непосредственно отсчеты АЦП? Ведь просуммировать целые числа получается гораздо быстрее, чем получить сумму "плавучих" чисел? К тому же при целочисленных вычислениях не накапливается ошибка округления. Поэтому алгоритм скользящего вычисления среднего можно значительно ускорить. Для этого нужно лишь хранить отдельно текущее значение суммы элементов буфера. При поступлении нового значения в буфер достаточно вычесть из этой суммы значение самого древнего элемента буфера, прибавить значение нового элемента буфера и вуаля! - вы получаете готовую сумму. Итого одно сложение и одно вычитание, вместо суммирования всего буфера. Для "плавучки" такой способ не годиться, т.к. довольно быстро набегает ошибка вычисления суммы. К тому же зачем вы каждый раз при записи нового значения в буфер сдвигаете все элементы в буфере? Это же лишнее время и совершенно ненужное действие. Сдвигайте не сам буфер, а лишь индекс-указатель на текущий элемент буфера. Получается то же самый неоднократно упомянутый циклический буфер. Код #define MAXNUMFLTRBUF 30
unsigned int funcSMA(unsigned int val) { static unsigned int fltrBuf[MAXNUMFLTRBUF]; static unsigned int idx; static unsigned long sumBuf; sumBuf-=fltrBuf[idx]; //вычитаем из суммы значение самого старого элемента sumBuf+=val; //прибавляем к сумме значение нового элемента fltrBuf[idx]=val; //записываем в буфер значение нового элемента if (idx<(MAXNUMFLTRBUF-1))//сдвигаем индекс idx++; else idx=0; return(sumBuf/MAXNUMFLTRBUF);//высисляем среднее значение } При желании можно добавить команды управления фильтром. Например, команду очистки буфера или инициализация его определенным значением. Для того, чтобы увеличить начальную крутизну его переходной характеристики. Код #define MAXNUMFLTRBUF 30
unsigned int funcSMA(unsigned int cmd, unsigned int val) { static unsigned int fltrBuf[MAXNUMFLTRBUF]; static unsigned int idx; static unsigned long sumBuf; if (cmd==0) //это случано не команда инициализации фильтра? { sumBuf-=fltrBuf[idx]; //нет, вычитаем старое значение sumBuf+=val; //прибавляем к сумме значение нового элемента fltrBuf[idx]=val; //записываем в буфер значение нового элемента if (idx<(MAXNUMFLTRBUF-1))//сдвигаем индекс idx++; else idx=0; return(sumBuf/MAXNUMFLTRBUF);//вычисляем среднее значение } else //да, инициализируем буфер фильтра новым значением { sumBuf=val*MAXNUMFLTRBUF; idx=MAXNUMFLTRBUF-1; while (idx!=0) fltrBuf[idx--]=val; return(val); } } Учитывая, что входные значения 12-и разрядные и сумма заведомо не превышает разрядности типа long, то при целочисленном вычислении среднего можно повысить точность, если перед делением сдвигать сумму до заполнения разрядности типа, а потом сдвигать обратно. Код return(((sumBuf<<8UL)/MAXNUMFLTRBUF)>>8UL);
|
|
|
|
|
Aug 31 2008, 16:48
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Aug 31 2008, 01:04)  Есть два способа решения этой проблемы. Либо на время вычислений в основном цикле запрещать прерывания от АЦП (либо останавливать его) - все равно АЦП не работает, когда вы считаете в прерывании. А разве не тож на тож получится?  Та же самая потеря времени... Единственное, что таймер не будет пробрасывать свои прерывания и будет четко вести счетчик прерываний... но лишние 10-20мс до начала короткого импульса - вроде не проблема... Цитата(rezident @ Aug 31 2008, 01:04)  Либо использовать два циклических буфера. В то время пока вы обсчитываете результаты одного буфера, в другой буфер (по прерываниям АЦП) идет запись новых результатов изменений. В основном цикле вы только устанавливаете номер буфера и флаг сигнализирующий обработчику прерывания АЦП о смене номера буфера. В прерывании АЦП сначала анализируется состояние флага и при необходимости изменяется указатель на буфер, а запись новых значений происходит уже в другой буфер. Ух. Жестко! Ладно, спасибо, попробую. Цитата(rezident @ Aug 31 2008, 01:04)  Значит у вас где-то ошибка в алгоритме. нужно синхронизировать обновления значений переменных координат так, чтобы не было разбега, как он у вас описан. Нужно. Вопрос - как Цитата(rezident @ Aug 31 2008, 01:04)  А вам приниципально усреднять уже готовые значения координат или можно усреднять непосредственно отсчеты АЦП? Э-э... а я вроде именно отсчеты АЦП и усредняю...  так мне казалось... Цитата(rezident @ Aug 31 2008, 01:04)  Для этого нужно лишь хранить отдельно текущее значение суммы элементов буфера. При поступлении нового значения в буфер достаточно вычесть из этой суммы значение самого древнего элемента буфера, прибавить значение нового элемента буфера и вуаля! - вы получаете готовую сумму. Точно! Спасибо большое! Действительно, куча лишних действий получалась! Только немного не ясно, почему строка sumBuf-=fltrBuf[idx];- это вычитание самого старого элемента  Вы записываете новый элемент fltrBuf[idx]=val; и сдвигаете счетчик - idx++. Значит, при поступлении следующего элемента fltrBuf[idx] будет хранить значение предыдущего, а не самого старого... Поясните, пожалуйста. Цитата(rezident @ Aug 31 2008, 01:04)  Учитывая, что входные значения 12-и разрядные и сумма заведомо не превышает разрядности типа long, то при целочисленном вычислении среднего можно повысить точность, если перед делением сдвигать сумму до заполнения разрядности типа, а потом сдвигать обратно. Код return(((sumBuf<<8UL)/MAXNUMFLTRBUF)>>8UL); А почему это повышает точность? как всегда спасибо за комментарии!
|
|
|
|
|
Aug 31 2008, 18:39
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Aug 31 2008, 22:48)  Нужно. Вопрос - как  Как обычно. С помощью буферов. Две переменные которые будут обновляться одновременно и считывание значений из которых не будет прерываться вычислениями. Цитата(Daria @ Aug 31 2008, 22:48)  Э-э... а я вроде именно отсчеты АЦП и усредняю...  так мне казалось...  Опс! Это я ошибся  Извиняюсь! Почему-то отложилось в памяти, что у вас там фильтрация плавающих числе идет, а страницу с вашим кодом еще раз посмотреть поленился. Цитата(Daria @ Aug 31 2008, 22:48)  Только немного не ясно, почему строка sumBuf-=fltrBuf[idx];- это вычитание самого старого элемента  Вы записываете новый элемент fltrBuf[idx]=val; и сдвигаете счетчик - idx++. Значит, при поступлении следующего элемента fltrBuf[idx] будет хранить значение предыдущего, а не самого старого... Поясните, пожалуйста. В полностью заполненном циклическом буфере фильтра индекс указывает на тот элемент, куда нужно записать новое значение, которое замещает самое старое значение. Следовательно индекс указывает на самое старое значение. Пример. Буфер из трех элементов. Поток состоит из последовательности натуральных чисел. Код | значения значение | элементов индекса | буфера --------------------------- 0 -, -, - - буфер пуст 1 1, -, - - 1 элемент, единственный и самый старый, индекс указывает на следующий, но буфер ЕЩЕ НЕ ЗАПОЛНЕН 2 1, 2, - - 2 элемента, самый старый 1-й, буфер ЕЩЕ НЕ ЗАПОЛНЕН, индекс указывает на следующий 0 1, 2, 3 - 3 элемента, буфер заполнен!!!, индекс указывает на следующий (1-й элемент), который является самым старым 1 4, 2, 3 - 3 элемента, буфер заполнен, индекс указывает на следующий (2-й элемент). который является самым старым 2 4, 5, 3 - 3 элемента, буфер заполнен, индекс указывает на следующий (3-й элемент). который является самым старым 0 4, 5, 6 - ну и т.д. 1 7, 5, 6 Цитата(Daria @ Aug 31 2008, 22:48)  А почему это повышает точность? Это особенности целочисленной математики. Хотя пожалуй в данном конкретном случае (единственная операция в выражении) это рояли не играет. Я просто перестраховался. Отставить сдвиги!
|
|
|
|
|
Aug 31 2008, 20:29
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Aug 31 2008, 22:39)  В полностью заполненном циклическом буфере фильтра индекс указывает на тот элемент, куда нужно записать новое значение, которое замещает самое старое значение. Ага, ясно. Спасибо за очень подробное разъяснение Цитата(rezident @ Aug 31 2008, 22:39)  Это особенности целочисленной математики. Хотя пожалуй в данном конкретном случае (единственная операция в выражении) это рояли не играет. Я просто перестраховался. Отставить сдвиги!  Есть "отставить сдвиги" Я теперь неделю буду в отпуске  так что можете от меня недельку отдохнуть а через неделю жду вас с новыми ответами на новые вопросы
|
|
|
|
|
Sep 12 2008, 18:04
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Ну вот, отпуск кончился, пошли суровые будни И вопросы Такая беда. У меня был iar, версия 4.11в - тридцатидневная демо-версия с официального сайта. Срок лицензии истек, я снесла эту версию, скачала опять и поставила снова. И вот фиг. Удаляю отовсюду, откуда только можно, но в реестр он прописался в закрытую область, которую нельзя удалить. Теперь сколько новый iar не ставь, все бестолку Как быть? переустанавливать винду не хочется. Попробовать какую-нибудь другую версию? Где взять? У меня был ломаный iar 3.2, но он почему-то жутко глючил. Можно как-нибудь почистить и закрытые области? Вот такая проблема, немного не по теме, но печальная
|
|
|
|
|
Sep 13 2008, 17:31
|

Беспросветный оптимист
     
Группа: Свой
Сообщений: 4 640
Регистрация: 26-12-07
Из: Н.Новгород
Пользователь №: 33 646

|
Цитата(rezident @ Sep 13 2008, 20:48)  А вы сами-то при передаче заказчику исходников своей программы строго следуете условиям лицензии GPL?  Заказчику мы обычно передаём только паспорт и РЭ, в крайнем случае схему. А вообще, как говорил кот Матроскин, холодильник чей - государственный, а холод, который он вырабатывает - наш. Так же и тут. Компилятор - GPL, а код, который он вырабатывает - наш.
--------------------
Программирование делится на системное и бессистемное. ©Моё :) — а для кого-то БГ — это Bill Gilbert =)
|
|
|
|
|
Sep 13 2008, 20:13
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(MrYuran @ Sep 13 2008, 23:31)  А вообще, как говорил кот Матроскин, холодильник чей - государственный, а холод, который он вырабатывает - наш.
Так же и тут. Компилятор - GPL, а код, который он вырабатывает - наш. Вы явно не читали или читали невнимательно текст GNU GENERAL PUBLIC LICENSE  Цитата(Сергей Борщ @ Sep 14 2008, 00:20)  Насколько я понимаю, лицензия GPL предписывает предоставлять по первому требованию исходники самого компилятора, но не код, им произведенный. При компиляции используются какие-то библиотеки компилятора, так? Так вот, если полученный в результате компиляции код содержит внутри себя хотя бы часть этой библиотеки (например, библиотеки вычислений с "плавучкой"), то весь он попадает под действие лицензии GPL. Если же какие-то части программы, имеющие возможность функционировать самостоятельно и не содержащие компоненты, попадающие под действие GPL, были лишь скомпилированы GNU-тым компилятором, тогда да, они также свободны от действия GPL. Давайте не будем засорять ветку  . Если вопрос лицензий free software и open source волнует кого-то, то можно завести отдельный топик.
|
|
|
|
|
Sep 15 2008, 17:38
|

фанат дивана
     
Группа: Свой
Сообщений: 3 387
Регистрация: 9-08-07
Из: Уфа
Пользователь №: 29 684

|
Цитата(rezident @ Sep 14 2008, 02:13)  При компиляции используются какие-то библиотеки компилятора, так? Так вот, если полученный в результате компиляции код содержит внутри себя хотя бы часть этой библиотеки (например, библиотеки вычислений с "плавучкой"), то весь он попадает под действие лицензии GPL. Библиотеки обычно не gpl, а lgpl. Но тут надо конечно быть очень внимательным... Цитата Давайте не будем засорять ветку  . Если вопрос лицензий free software и open source волнует кого-то, то можно завести отдельный топик. Я был бы не против почитать такой топик
--------------------
Если бы я знал, что такое электричество...
|
|
|
|
|
Sep 23 2008, 18:33
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Здравствуйте. Такие вопросы - сейчас я прошиваю контроллер самодельной отладкой, схему которой советовали, по-моему, вы, rezident - она питается с платы, 3,3 В. В дальнейшем хочу использовать MSP-FET430PIF, так как требования - либо на свою отладку оформлять докуметацию по всем правилам, либо пользоваться готовой. и вот вопрос - у MSP-FET430PIF питание от LPT - мне сказали, что это опасно, что LPT часто горят, если программатор питается прямо от порта. Это правда? насколько часто?  Может, лучше не покупать этот MSP-FET430PIF? И еще - не совсем в по теме. Не подскажите какой-нибудь кварцевый резонатор на 8MГц, как можно меньших размеров? И, желательно, планарный  Не могу найти, на такую частоту все какие-то здоровые, а нужно сокращать размеры платы
|
|
|
|
|
Sep 23 2008, 19:56
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Sep 24 2008, 00:33)  В дальнейшем хочу использовать MSP-FET430PIF, так как требования - либо на свою отладку оформлять докуметацию по всем правилам, либо пользоваться готовой. и вот вопрос - у MSP-FET430PIF питание от LPT - мне сказали, что это опасно, что LPT часто горят, если программатор питается прямо от порта. Это правда? насколько часто?  Может, лучше не покупать этот MSP-FET430PIF? Согласен. MSP-FET430PIF покупать не нужно в любом случае  . Если вам нужен индивидуальный отладчик и программатор, то купите лучше MSP-FET430UIF или его полнофункциональный клон MSPLink. Olimex-овский USB-FET покупать не рекомендую. Если же вы хотите передать прошивку кристаллов на производство, то лучше сразу купить MSP-GANG430. Мы покупали в Терраэлектронике. Преимущества: - можно программировать до 8ми кристаллов одновременно. - все кристаллы можно запитать непосредственно от MSP-GANG430. - поддерживает функцию прожигания fuse. - можно загрузить прошивку в программатор и использовать его автономно, т.е. он не требует обязательного наличия компьютера. С компьютера вы только загружаете образ прошивки и конфигурацию программирования через последовательный интерфейс RS-232. Соответственно при отсутствии в компе RS-232 можно дополнить программатор любым готовым кабелем USB-COM и работать дальше. В случае же LPT-ного MSP-FET430PIF и отсутствия LPT-порта вы получите массу трудностей. - MSP-GANG430 поддерживает все предыдущие и последующие серии MSP430, т.е. у него есть перспективы техподдержки. Например, аналогичный по функциям MSP-PRGS430 уже сейчас не поддерживает интерфейс Spy-Bi-Wire и некоторые кристаллы из серии MSP430x2xx. - чуть не забыл упомянуть главное достоинство MSP-GANG430 для производства, на котором не всегда адекватные по знаниям люди работают  У MSP-GANG430 из органов управления всего ОДНА кнопка!  Из индикации два ряда светодиодов (красные и зеленые). Обучить нажатию на кнопку можно даже неквалифицированный персонал. Кстати, раз вы планируете программировать кристаллы уже запаянные в плату, то обдумайте стоит ли для этого использовать именно JTAG? Я, например, в серийных устройствах на MSP430 вывожу для программирования сигналы BSL. Во-первых, требуется меньше контактов (всего 6). Во-вторых, BSL позволяет и в дальнейшем менять firmware, когда уже установлена защита (fuse уже пережжен) и JTAG не работает. Цитата(Daria @ Sep 24 2008, 00:33)  И еще - не совсем в по теме. Не подскажите какой-нибудь кварцевый резонатор на 8MГц, как можно меньших размеров? И, желательно, планарный  Не могу найти, на такую частоту все какие-то здоровые, а нужно сокращать размеры платы  Посмотрите резонаторы фирм Geyer Electronic и Golledge. Мы часовой (32768Гц) KX-327LT от Geyer недавно начали применять, размеры его всего 1,5х7мм. Продукцией Geyer Electronic торгует Компел и Терраэлектроника, а продукцию Golledge представляет фирма ЭФО.
|
|
|
|
|
Sep 23 2008, 20:01
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(Daria @ Sep 23 2008, 21:33)  мне сказали, что это опасно, что LPT часто горят, если программатор питается прямо от порта. Это правда? Нет, неправда. Горит не от питания, а от бестолковости питающего. Когда корпус компьютера не заземлен, когда втыкают в запитанное устройство, питающееся от незаземленного блока питания, ну и т.д. Для MSP-FET430PIF питание от порта - один из штатных режимов. Цитата(Daria @ Sep 23 2008, 21:33)  Может, лучше не покупать этот MSP-FET430PIF? Да. Лучше купите MSP-FET430UIF. Это почти то же самое, только от USB. И питание от него тоже можно брать. Цитата(Daria @ Sep 23 2008, 21:33)  Не подскажите какой-нибудь кварцевый резонатор на 8MГц, как можно меньших размеров? Выбирайте: "Чип И Дип", Кварцевые резонаторы (Всего товаров: 357)
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Sep 23 2008, 20:12
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Ага, но мне, как пока неквалифицированному программисту, хотелось бы все же программить именно на плате, сразу непосредсственно смотреть сигналы с датчиков. Возможность BSL заложу, но с JTAG мне гораздо проще - удобно отлаживать все это дело, то так, то так попробовать  А с полноценным отладчиком не знаю, как и работать  такой парадокс. Пробовать ведь надо на реальной плате  а так - вставить кристалл и... чего дальше?  Мое парадоксальное мышление проявляется тут с новой силой  Когда прога будет уже окончательно выверена, тогда, конечно - вставил кристалл, прошил - давай следующий. Но окончательно - это еще неизвестно когда, к тому же задание постоянно меняется Значит PIF - не пойдет? A UIF - разве позволяет программировать прямо на плате? Резонаторы посмотрю, спасибо
Сообщение отредактировал Daria - Sep 23 2008, 20:53
|
|
|
|
|
Sep 24 2008, 17:18
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(Сергей Борщ @ Sep 24 2008, 01:52)  Эээ... А вы его случайно с ez430 (USB-"свистком") не перепутали? Ага, точно  Теперь понятно, спасибо. Кварц, кстати, подходящий нашла у Murata - CSTCE8M0G53 - нормальный? Теперь дальше. rezident, пытаюсь воспроизвести что-то вроде протоколоа SLIP, который вы упоминали. Вот текст. Код #define sizeBufTx 16 #define sizeBufRx 16
unsigned char bufRx[sizeBufRx]; unsigned char bufTx[sizeBufTx];
typedef struct CharBuffer { unsigned char* Data; int NData; int cData; int pWrite; int pRead; } CharBuffer; void BufferReset(CharBuffer* pBuffer) { pBuffer->pWrite = 0; pBuffer->pRead = 0; pBuffer->cData = 0; } int BufferWrite(CharBuffer* pBuffer,unsigned char Byte) { if(pBuffer->cData == pBuffer->NData - 1) return 1; pBuffer->Data[pBuffer->pWrite] = Byte; if(pBuffer->pWrite == pBuffer->NData - 1) pBuffer->pWrite = 0; else pBuffer->pWrite++; pBuffer->cData++; return 0; } int BufferRead(CharBuffer* pBuffer, unsigned char* Byte) { if(pBuffer->cData == 0) return 1; *Byte = pBuffer->Data[pBuffer->pRead]; if(pBuffer->pRead == pBuffer->NData - 1) pBuffer->pRead = 0; else pBuffer->pRead++; pBuffer->cData--; return 0; }
struct CharBuffer UARTTxBuf, UARTRxBuf; void WriteTxbuf(int* data, char size) { unsigned char a, j; if ((IE1 & UTXIE0) == 0) { BufferWrite(&UARTTxBuf, 0xC0); for(j = 0; j < size; j++) { a = data[j] & 0xFF; switch (a) { case 0xC0: { BufferWrite(&UARTTxBuf, 0xDB); BufferWrite(&UARTTxBuf, 0xDC); break; } case 0xDB: { BufferWrite(&UARTTxBuf, 0xDB); BufferWrite(&UARTTxBuf, 0xDD); break; } default: BufferWrite(&UARTTxBuf, a); } a = data[j] >> 8; switch (a) { case 0xC0: { BufferWrite(&UARTTxBuf, 0xDB); BufferWrite(&UARTTxBuf, 0xDC); break; } case 0xDB: { BufferWrite(&UARTTxBuf, 0xDB); BufferWrite(&UARTTxBuf, 0xDD); break; } default: BufferWrite(&UARTTxBuf, a); } BufferWrite(&UARTTxBuf, 0xC0); } IE1 |= UTXIE0; IFG1 |= UTXIFG0; } }
void ReadRxbuf(unsigned char *data) { IE1 &= ~URXIE0; BufferRead(&UARTRxBuf, data); IE1 |= URXIE0; } Потом в main Код char i, txsize; unsigned char rxdata; int txdata[sizeBufTx]; BufferReset(&UARTRxBuf); UARTRxBuf.NData = sizeBufRx; UARTRxBuf.Data = bufRx; BufferReset(&UARTTxBuf); UARTTxBuf.NData = sizeBufTx; UARTTxBuf.Data = bufTx; Ну. и вся остальная настройка всего , потом Код IE1 |= URXIE0; _BIS_SR(GIE); for(;;) { rxdata = 0; txsize = 0; ReadRxbuf(&rxdata); if (rxdata) { txdata[txsize++] = rxdata;
WriteTxbuf(txdata, txsize); } } }
#pragma vector = UART0RX_VECTOR __interrupt void usart0_rx (void) { BufferWrite(&UARTRxBuf, U0RXBUF); }
#pragma vector = USART0TX_VECTOR __interrupt void usart0_tx (void) { unsigned char data; if (!BufferRead(&UARTTxBuf, &data)) U0TXBUF = data; else IE1 &= ~UTXIE0; } Байты C0 - END, начало и конец посылки, DB - ESC. Если такой байт встречается в самой посылке, то он заменяется на два - DB,DС или DB, DD - ну, вы-то уж. наверное, знаете этот протокол. В чем проблема - вроде бы все работает, но - при сбросе питания всегда присылается 0 или 0,1. При включении - тоже. откуда это берется - не понятно  И в первой после включения питания посылке теряется первый стартовый C0 - т.е. теряется-то вся посылка, а это недопустимо для меня - моя плата должна отсылать управляющие команды на другую - потеря одной команды приведет к проблемам. почему теряется - тоже никак не могу понять Вот. Будет время - спаите. помогите
|
|
|
|
|
Sep 24 2008, 18:12
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Я не вижу функции инициализации UART, но попробуйте перед тем как разрешать прерывание от приемника UART (по)чистить его флаги. В строках Цитата IE1 |= UTXIE0; IFG1 |= UTXIFG0; следует поменять их очередность. Кроме того, функции работы с буфером у вас не полностью реентерабельные. Они не обеспечивают атомарности доступа к переменным индекса и счетчика.
|
|
|
|
|
Sep 24 2008, 19:14
|
Частый гость
 
Группа: Участник
Сообщений: 92
Регистрация: 1-06-08
Пользователь №: 37 959

|
Цитата(Daria @ Sep 23 2008, 22:33)  Не подскажите какой-нибудь кварцевый резонатор на 8MГц, как можно меньших размеров? А не лучше ли использовать DCO с внешним резистором, если толщина схемы важнее стабильности частоты?
|
|
|
|
|
Sep 25 2008, 06:57
|

Профессионал
    
Группа: Модераторы
Сообщений: 1 120
Регистрация: 17-06-04
Пользователь №: 37

|
Цитата Кварц, кстати, подходящий нашла у Murata - CSTCE8M0G53 - нормальный? Мы используем Муратовские керамические трёхногие SMD-шные резонаторы с установлеными в них емкостями. CSTCE8M00G55A-R0 - на 8МГц CSTCR6M00G53-R0 - на 6МГц И довольны.
Murata_Ceramic_Resonators__CERALOCK_.pdf ( 609.11 килобайт )
Кол-во скачиваний: 276
--------------------
Если зайца бить, его можно и спички научить зажигать Сколько дурака не бей - умнее не будет. Зато опытнее
|
|
|
|
|
Sep 25 2008, 18:43
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Sep 24 2008, 22:12)  Я не вижу функции инициализации UART, но попробуйте перед тем как разрешать прерывание от приемника UART (по)чистить его флаги. В строках
следует поменять их очередность. Кроме того, функции работы с буфером у вас не полностью реентерабельные. Они не обеспечивают атомарности доступа к переменным индекса и счетчика. Ну, теряться байты перестали, спасибо. Но нули все равно приходят при включении питания. Не могу понять, откуда они берутся  а функции, значит, не очень? Цитата(LCD @ Sep 24 2008, 23:14)  А не лучше ли использовать DCO с внешним резистором, если толщина схемы важнее стабильности частоты? Не, не лучше. Во-первых, хочется все же высокой частоты, от DCO - это 800кГц, а, во-вторых, не важнее, конечно, просто уменьшение размеров желательно Цитата(VAI @ Sep 25 2008, 10:57)  Мы используем Муратовские керамические трёхногие SMD-шные резонаторы с установлеными в них емкостями. CSTCE8M00G55A-R0 - на 8МГц CSTCR6M00G53-R0 - на 6МГц И довольны Спасибо! Я так и думала, что это хорошие  Надеюсь, мы тоже будем довольны
|
|
|
|
|
Sep 25 2008, 19:30
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Sep 26 2008, 00:43)  Ну, теряться байты перестали, спасибо. Но нули все равно приходят при включении питания. Не могу понять, откуда они берутся  Покажите функцию инициализации UART. Цитата(Daria @ Sep 26 2008, 00:43)  Не, не лучше. Во-первых, хочется все же высокой частоты, от DCO - это 800кГц, а, во-вторых, не важнее, конечно, просто уменьшение размеров желательно Вы опять что-то недочитали в User's Guide  Частоту DCO можно изменить программно вплоть до 6,5МГц. А если использовать внешний резистор ROSC, то DCO можно легко разогнать МГц эдак до 50 одной левой.  На такой частоте работать кристалл конечно же не сможет, но DCO такую частоту генерировать будет. Самолично проверял  Проблема с DCO в том, что частота его зависит от температуры и напряжения питания. Для связи требуется стабильность битовой частоты не хуже 1-2%, 0,5% это вообще для любого случая подойдет. Если напряжения питания стабильно, используется внешний резистор ROSC с хорошим ТКС (не хуже скажем 50ppm/°C), DCO при старте калибруется по часовому кварцу (а при возникновении ошибок по связи происходит перекалибровка), то вполне можно обойтись и без внешнего высокочастотного кварца, одним лишь часовым.
|
|
|
|
|
Sep 26 2008, 15:21
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Sep 25 2008, 23:30)  Покажите функцию инициализации UART. Код U0CTL |= CHAR; // 8-bit character U0TCTL = SSEL0; // UCLK = ACLK U0BR0 = 0xA0; // 8.00Mhz 19200 bps (416) U0BR1 = 0x01; U0MCTL = 0x00; // no modulation U0CTL &= ~SWRST; ME1 |= UTXE0 + URXE0; // Enable USART0 TXD/RXD IE1 |= URXIE0; Флаги не сбрасываю, потому что не помогло Цитата(rezident @ Sep 25 2008, 23:30)  Вы опять что-то недочитали в User's Guide  Видимо, да Цитата(rezident @ Sep 25 2008, 23:30)  Частоту DCO можно изменить программно вплоть до 6,5МГц. А если использовать внешний резистор ROSC, то DCO можно легко разогнать МГц эдак до 50 одной левой.  На такой частоте работать кристалл конечно же не сможет, но DCO такую частоту генерировать будет. Самолично проверял  Проблема с DCO в том, что частота его зависит от температуры и напряжения питания. Для связи требуется стабильность битовой частоты не хуже 1-2%, 0,5% это вообще для любого случая подойдет. Если напряжения питания стабильно, используется внешний резистор ROSC с хорошим ТКС (не хуже скажем 50ppm/°C), DCO при старте калибруется по часовому кварцу (а при возникновении ошибок по связи происходит перекалибровка), то вполне можно обойтись и без внешнего высокочастотного кварца, одним лишь часовым. Спасибо, буду иметь ввиду
|
|
|
|
|
Sep 26 2008, 15:33
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Ну вот она и ошибка. Первой командой инициализации USART должна быть установка бита SWRST, как это и указано в User's Guide. Вставьте в процедуру инициализации первой командой Код U0CTL = SWRST; А дальше по вашему тексту. Код U0CTL |= CHAR; // 8-bit character U0TCTL = SSEL0; // UCLK = ACLK U0BR0 = 0xA0; // 8.00Mhz 19200 bps (416) U0BR1 = 0x01; U0MCTL = 0x00; // no modulation ME1 |= UTXE0 | URXE0; // Enable USART0 TXD/RXD U0CTL &= ~SWRST; IE1 |= URXIE0; и все лишние нули пропадут.
Эскизы прикрепленных изображений
|
|
|
|
|
Sep 29 2008, 19:16
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Sep 26 2008, 19:33)  Ну вот она и ошибка. Первой командой инициализации USART должна быть установка бита SWRST, как это и указано в User's Guide. Вставьте в процедуру инициализации первой командой Код U0CTL = SWRST; не-а, не помогло
|
|
|
|
|
Sep 30 2008, 19:00
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Sep 30 2008, 00:23)  Делайте функции работы с буфером реентерабельными. Потому, что работа с ними в том числе из прерываний идет. По крайней мере атомарность доступа к указателям и счетчику буфера обеспечить нужно. Кстати, что у вас такое NData и cData в структуре буфера? NData - максимальный размер буфера, инициализируется UARTTxBuf.NData = sizeBufTx(16), СData - счетчик. Такое дело. Как выяснилось, такая работа буфера тоже занимает кучу лишнего времени, так как байты загружаются в буфер, только после того, как будет отправлена предыдущая посылка if ((IE1 & UTXIE0) == 0) Т.е. пока 6(в данном случае) байт не передастся, запись в буфер не произойдет. Это же не правильно?  Хочу сделать так, что бы запись в буфер и отправка были независимы. Т.е. теперь Код int WriteTxbuf(int* data, char size) { unsigned char a, j; // BufferReset(&UARTTxBuf);
if((UARTTxBuf.NData-UARTTxBuf.cData)<6)return -1;
BufferWrite(&UARTTxBuf, 0xC0); for(j = 0; j < size; j++) { a = data[j] & 0xFF; switch (a) { case 0xC0: { BufferWrite(&UARTTxBuf, 0xDB); BufferWrite(&UARTTxBuf, 0xDC); break; } case 0xDB: { BufferWrite(&UARTTxBuf, 0xDB); BufferWrite(&UARTTxBuf, 0xDD); break; } default: BufferWrite(&UARTTxBuf, a); } a = data[j] >> 8; switch (a) { case 0xC0: { BufferWrite(&UARTTxBuf, 0xDB); BufferWrite(&UARTTxBuf, 0xDC); break; } case 0xDB: { BufferWrite(&UARTTxBuf, 0xDB); BufferWrite(&UARTTxBuf, 0xDD); break; } default: BufferWrite(&UARTTxBuf, a); } } BufferWrite(&UARTTxBuf, 0x3F); return 0; } А в основной порграмме Код if(state!=(-1)){ ReadRxbuf(&rxdata); txsize = 0; txdata[txsize++] = -4; txdata[txsize++] = -6; } if(WriteTxbuf(txdata, txsize)) state=-1; else state=0;
if ((IE1 & UTXIE0) == 0) { IFG1 |= UTXIFG0; IE1 |= UTXIE0; } Почему <6? Потому, что сейчас посылки по 6 байт. Т.е., запись происходит только если есть место для следующей посылки(6 байт), а отправка начинается, если предыдущая посылка отправлена - эти два действия независимы. Но - в отладчике все хорошо, а на практике, при большой скорости передачи(т.е. когда идет непрерывная загрузка в буфер), последний байт в посылке теряется. Отчего это происходит?  Если делать задержку между посылками, то все нормально, но это же жутко не функционально. тогда вообще нет смысла в буфере  а, кстати, не имеет ли смысла перед сбросом IE1 &= ~UTXIE0 проверять бит UTXEPT? такой вопрос
Сообщение отредактировал Daria - Sep 30 2008, 19:04
|
|
|
|
|
Sep 30 2008, 19:49
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Oct 1 2008, 01:00)  NData - максимальный размер буфера, инициализируется UARTTxBuf.NData = sizeBufTx(16), СData - счетчик. Шо це таке "sizeBufTx(16)"?  Функция? Или размер буфера? Цитата(Daria @ Oct 1 2008, 01:00)  Такое дело. Как выяснилось, такая работа буфера тоже занимает кучу лишнего времени, так как байты загружаются в буфер, только после того, как будет отправлена предыдущая посылка if ((IE1 & UTXIE0) == 0) Т.е. пока 6(в данном случае) байт не передастся, запись в буфер не произойдет. Это же не правильно?  Хочу сделать так, что бы запись в буфер и отправка были независимы. Дык буфер-то один или буферы для записи и для чтения разные? Если даже один, но размер его больше 12, то почему мешает одно другому? Цитата(Daria @ Oct 1 2008, 01:00)  Если делать задержку между посылками, то все нормально, но это же жутко не функционально. тогда вообще нет смысла в буфере  Раз у вас канал полнодуплексный и прием с передачей независимы, то сделайте вы два разных буфера и всех делов. Цитата(Daria @ Oct 1 2008, 01:00)  а, кстати, не имеет ли смысла перед сбросом IE1 &= ~UTXIE0 проверять бит UTXEPT? Не имеет. Запрет прерываний не влияет ни на текущую передачу байта, находящегося в сдвиговом регистре, ни на передачу следующего байта, находящегося в буфере передатчика USART. По поводу кода. Извините, но я уже перестал что-либо понимать в этих огрызках функций  А отрицательные числа, возвращаемые функцией (как код ошибки?), вместо привычных 0 и 1 или чисел натурального ряда, это вообще нечто
|
|
|
|
|
Sep 30 2008, 20:26
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Код typedef struct CharBuffer { unsigned char* Data; int NData; int cData; int pWrite; int pRead; } CharBuffer;
#define sizeBufTx 16 struct CharBuffer UARTTxBuf; unsigned char bufTx[sizeBufTx];
#define sizeBufRx 16 struct CharBuffer UARTRxBuf; unsigned char bufRx[sizeBufRx]; почти весь код два сообщения назад Раньше делала отправку примерно так, как вы советовали(ссобщений эдак 7-8 назад), if ((IE1 & UTXIE0) == 0) запись в буфер IFG1 |= UTXIFG0; IE1 |= UTXIE0; В обработчике прерываний Код #pragma vector = USART0TX_VECTOR __interrupt void usart0_tx (void) { unsigned char data; if (!BufferRead(&UARTTxBuf, &data)) U0TXBUF = data; else IE1 &= ~UTXIE0; } но так получается, что запись следующей посылки идет только после того, как отправится текущая посылка , условие if ((IE1 & UTXIE0) == 0) - куча времени впустую. но я пониаю, что вам некогда разбираться во всем этом  Ну и ладно, и так помогли уже нехило  разберусь. надеюсь А что касается return -1... случайно  return 1, но по большому-то счету есть разница?  смысл такой, что не ноль.
|
|
|
|
|
Sep 30 2008, 23:05
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Oct 1 2008, 02:26)  но так получается, что запись следующей посылки идет только после того, как отправится текущая посылка , условие if ((IE1 & UTXIE0) == 0) - куча времени впустую. Почему впустую? if это ведь не while, т.е. если условие не выполняется идем дальше, а не ждем его выполнения. Цитата(Daria @ Oct 1 2008, 02:26)  но я пониаю, что вам некогда разбираться во всем этом  Ну и ладно, и так помогли уже нехило  разберусь. надеюсь  тут уже сам замысел, алгоритм непонятен. То ли вы реализуете пакетный протокол, который подразумевает запрос-ответ? То ли у вас информационный канал с контролем управления, построенный в "обертке" пакетного протокола? Если пакетный протокол точка-точка, то все так и должно быть: сначала передача запроса, затем прием ответа. Если же нужно постоянно гнать поток информации, лишь изредка, получая какие-то команды управления, то при наличии раздельных буферов чтения и записи, прерывание передатчика не нужно запрещать вообще. И опрос бита TXEPT тогда как раз нужен перед тем, как программно установить TXIFG для инициации прерываний. При передаче работайте непосредственно с кольцевым буфером передатчика записывая в него по факту его опустошения. Т.е. в майне, где формируется пакет, счетчик буфера лишь увеличивается, а в прерывании передатчика счетчик буфера уменьшается. Индекс, естественно, только инкрементируется, учитывая переход его в начало буфера. С буфером приемника аналогично, но наоборот. В прерывании приемника счетчик буфера увеличивается, а в майне вы буфер вычитываете и уменьшаете счетчик буфера. Прерывания ни приемника, ни передатчика при при этом запрещать не нужно. В прерывании приемника запись в буфер по факту наличия свободного места происходит.
|
|
|
|
|
Oct 1 2008, 18:23
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Oct 1 2008, 03:05)  Почему впустую? if это ведь не while, т.е. если условие не выполняется идем дальше, а не ждем его выполнения. Да, но данные обновляются очень быстро, а запись в буфер не идет, пока не отправится предыдущая посылка, в результате данные теряются и сам буфер немного теряет смысл Цитата(rezident @ Oct 1 2008, 03:05)  тут уже сам замысел, алгоритм непонятен. То ли вы реализуете пакетный протокол, который подразумевает запрос-ответ? То ли у вас информационный канал с контролем управления, построенный в "обертке" пакетного протокола? Если пакетный протокол точка-точка, то все так и должно быть: сначала передача запроса, затем прием ответа. Если же нужно постоянно гнать поток информации, лишь изредка, получая какие-то команды управления, то при наличии раздельных буферов чтения и записи, прерывание передатчика не нужно запрещать вообще. Будут две платы, на одной пакетный протокол запрос-ответ, на другой - постоянный поток информации, изредка получая команды  пока второе Цитата(rezident @ Oct 1 2008, 03:05)  И опрос бита TXEPT тогда как раз нужен перед тем, как программно установить TXIFG для инициации прерываний. При передаче работайте непосредственно с кольцевым буфером передатчика записывая в него по факту его опустошения. Т.е. в майне, где формируется пакет, счетчик буфера лишь увеличивается, а в прерывании передатчика счетчик буфера уменьшается. Индекс, естественно, только инкрементируется, учитывая переход его в начало буфера. С буфером приемника аналогично, но наоборот. В прерывании приемника счетчик буфера увеличивается, а в майне вы буфер вычитываете и уменьшаете счетчик буфера. Прерывания ни приемника, ни передатчика при при этом запрещать не нужно. В прерывании приемника запись в буфер по факту наличия свободного места происходит. ну, вроде работает, спасибо
Сообщение отредактировал Daria - Oct 1 2008, 18:24
|
|
|
|
|
Oct 1 2008, 19:34
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Oct 2 2008, 00:23)  Да, но данные обновляются очень быстро, а запись в буфер не идет, пока не отправится предыдущая посылка, в результате данные теряются и сам буфер немного теряет смысл  Я вам уже объяснял области применения буферов: а) синхронизация асинхронных потоков, б) сглаживание неравномерностей поступления данных в потоках. Если у вас данные обновляются быстрее, чем уходят в канал связи и при этом требуется "не потерять" их, то это лишь означает, что у вас канал слишком "узкий" и "тормозной". Причем тут буфера?
|
|
|
|
|
Oct 4 2008, 17:01
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(VAI @ Oct 3 2008, 05:12)  Кстати, по уартам, я как-то выкладывал свои исходники работы с последовательными портами по прерыванию. Там использую циклические "буфера" :-) своего изготовления. http://electronix.ru/forum/index.php?showt...mp;#entry194257Может пригодится... Наверняка пригодится!  Большое спасибо, VAI
Сообщение отредактировал Daria - Oct 4 2008, 17:03
|
|
|
|
|
Oct 5 2008, 15:42
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Опять я  теперь нужен еще буфер - для АЦП. посмотрите, пожалуйста, нормально ли написана функция чтения? Та же структура Код typedef struct CharBuffer { unsigned char* Data; int NData; int cData; int pWrite; int pRead; } CharBuffer; #define sizeADCBufR 100 struct CharBuffer stADCBufR; unsigned char ADCBufR[sizeADCBufR];
stADCBufR.NData = sizeADCBufR; stADCBufR.Data = ADCBufR; BufferReset(&stADCBufR); Функция записи - записывает по байтам два int числа, функции записи и чтения байта описаны выше Код int WriteADCbuf(CharBuffer* pBuffer, int* data, int size) { if((pBuffer->NData-pBuffer->cData)<2*size+1)return 1; for( int j = 0; j < size; j++) { BufferWrite(pBuffer, (data[j]&0xFF)); BufferWrite(pBuffer, (data[j]>>8)); } return 0; } Чтение в две переменных Код void ReadADCbuf(CharBuffer* pBuffer, int* data0, int* data1) { unsigned char Dat[4]; BufferRead(pBuffer, &Dat[0]); BufferRead(pBuffer, &Dat[1]); *data0=((Dat[0]&0x00FF)|((Dat[1]<<8)&0xFF00)); BufferRead(pBuffer, &Dat[2]); BufferRead(pBuffer, &Dat[3]); *data1=((Dat[2]&0x00FF)|((Dat[3]<<8)&0xFF00)); } Вызов ReadADCbuf(&stADCBufR, &Vr0, &Vr1) Правильно ли склеиваю? И вообще - что-то с ней, по-моему, не то Еще вопрос - как правильно работать с WDT? Я просто останавливаю его при начальной инициализации, и все. Но говорят, что это не правильно, нужно постоянно обращаться к нему, чтобы избежать зависания процессора. Как это делать - обращаться постоянно в цикле for(;;)?
|
|
|
|
|
Oct 6 2008, 06:04
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(Daria @ Oct 5 2008, 22:35)  И еще  Мне нужно обмениваться с двумя устройствами. Обычно при обмене кто-то главный (master), а кто-то подчиненный (slave). Как старшинство распределено среди этого трио?
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Oct 6 2008, 17:45
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(Dog Pawlowa @ Oct 6 2008, 10:04)  Обычно при обмене кто-то главный (master), а кто-то подчиненный (slave). Как старшинство распределено среди этого трио? одно устройство принимает команды с компьютера(от оператора) и на основании их руководит вторым устройством(передает команды и принимает ответные данные).  Как-то так Вопрос про функцию снимается, а вот про WDT? Что, если перезапускать его в прерываниях таймера через каждые сколько-то мс? Как вообще обычно им пользуются?
|
|
|
|
|
Oct 6 2008, 19:04
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Oct 6 2008, 23:45)  одно устройство принимает команды с компьютера(от оператора) и на основании их руководит вторым устройством(передает команды и принимает ответные данные).  Как-то так Раз есть второй аппаратный UART, то грешнО его не использовать. Цитата(Daria @ Oct 6 2008, 23:45)  а вот про WDT? Что, если перезапускать его в прерываниях таймера через каждые сколько-то мс? Как вообще обычно им пользуются? Лично я его обычно использую как таймер системных тиков, т.к. у него приоритет прерывания один из самых высоких  Если же вас интересует теория, то где бы вы не сбрасывали WatchDog он должен сбрасываться только после анализа нескольких флагов, которые устанавливаются в разных ответственных местах программы. Вы можете сбрасывать WDT в прерывании таймера, но не просто по факту попадания в это прерывания, а только если выполняются все условия "попадания" в различные другие части программы. В те, куда программный код обязательно должен привести в промежутках между сбросом WDT. Кстати, от "защелкивания" или экстремальных случаев воздействия статики на MSP430 внутренний WatchDog не спасает.
|
|
|
|
|
Oct 7 2008, 17:09
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Oct 6 2008, 23:04)  Лично я его обычно использую как таймер системных тиков, т.к. у него приоритет прерывания один из самых высоких  Если же вас интересует теория, то где бы вы не сбрасывали WatchDog он должен сбрасываться только после анализа нескольких флагов, которые устанавливаются в разных ответственных местах программы. Вы можете сбрасывать WDT в прерывании таймера, но не просто по факту попадания в это прерывания, а только если выполняются все условия "попадания" в различные другие части программы. В те, куда программный код обязательно должен привести в промежутках между сбросом WDT. Кстати, от "защелкивания" или экстремальных случаев воздействия статики на MSP430 внутренний WatchDog не спасает. Ага, а я вот как раз хотела сначала настроить в сторожевом режиме, а потом просто сбрасывать WDTCNTCL в прерываниях таймера  Не все так просто... То есть, вы не используете WDT в сторожевом режиме, и ничего страшного не случается? И вот еще более важные вопросы - У меня куча массивов, если объявлять их в main, они начинают забивать стек, если как глобальные - то ведь память, отведенная под глобальные константы ограничена. Как быть? Можно ли как-то программно увеличить размер стека? Нужно либо увеличивать стек, либо как-то хитро объявлять все эти массивы... подскажите. и не ругайтесь, если опять сморозила глупость
|
|
|
|
|
Oct 7 2008, 17:41
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Oct 7 2008, 23:09)  Ага, а я вот как раз хотела сначала настроить в сторожевом режиме, а потом просто сбрасывать WDTCNTCL в прерываниях таймера  Не все так просто... То есть, вы не используете WDT в сторожевом режиме, и ничего страшного не случается?  А что там должно "страшного" случиться?  Режим интервального таймера для WDT является штатным и документированным. Если вы сомневаетесь в помехоустойчивости, то вообще-то помехоустойчивость устройства в бОльшей степени определяется конструктивными и схемотехническими (трассировка платы, расположение и подключение внешних и внутренних цепей и т.п.) особенностями устройства, а не использованием WatchDog  Цитата(Daria @ Oct 7 2008, 23:09)  И вот еще более важные вопросы - У меня куча массивов, если объявлять их в main, они начинают забивать стек, если как глобальные - то ведь память, отведенная под глобальные константы ограничена. Как быть? Можно ли как-то программно увеличить размер стека? Нужно либо увеличивать стек, либо как-то хитро объявлять все эти массивы... подскажите. и не ругайтесь, если опять сморозила глупость  В Plain C и ANSI C используются четыре типа переменных: 1. register (регистровые), размещаются в регистрах (хотя компиляторы чаще всего плюют на это указание). Область "видимости" текущая функция/контейнер. При выходе из функции/контейнера не сохраняются. 2. auto (автоматические или локальные), размещаются на стеке (или в регистрах, опять же по прихоти компилятора). Область "видимости" текущая функция, контейнер. При выходе из функции/контейнера не сохраняются. 3. global (глобальные), размещаются на постоянной основе в ОЗУ, в области данных. Область видимости весь текущий модуль. А объявленные с квалификатором extern могут быть "видны" в любом другом модуле проекта. 4. static (статические), размещаются на постоянной основе в ОЗУ, в области данных. Область "видимости" текущая функция (если объявление внутри функции) или текущий модуль (если объявление как глобальной снаружи какой-либо функции). При выходе из функции сохраняются. Но статические переменные не могут быть использованы в других модулях проекта и квалификатор extern к ним неприменим. В IAR для MSP430 принято, что область данных (глобальных и статических переменных) располагается "снизу" ОЗУ, а стек "сверху" и "растет" ей навстречу. Для того, чтобы хоть как-то контролировать стек в Plain C, вы можете сами организовать "кучу" (HEAP). Для этого создайте большой глобальный массив и располагайте свои временные "массивчики" внутри его. Но для этого нужно будет научиться (если еще не научились) пользоваться указателями, обязательно контролируя выходы их (указателей) за границы выделенных "массивчиков" и глобального массива кучи. А вообще в map-файле, который можно генерировать с помощью конфигурирования опций IAR, указан размер стека при использовании каждой функции.
|
|
|
|
|
Oct 7 2008, 18:12
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Oct 7 2008, 21:41)  В IAR для MSP430 принято, что область данных (глобальных и статических переменных) располагается "снизу" ОЗУ, а стек "сверху" и "растет" ей навстречу. Для того, чтобы хоть как-то контролировать стек в Plain C, вы можете сами организовать "кучу" (HEAP). Для этого создайте большой глобальный массив и располагайте свои временные "массивчики" внутри его. Но для этого нужно будет научиться (если еще не научились) пользоваться указателями, обязательно контролируя выходы их (указателей) за границы выделенных "массивчиков" и глобального массива кучи. А вообще в map-файле, который можно генерировать с помощью конфигурирования опций IAR, указан размер стека при использовании каждой функции. Все же пока не ясно  Есть массивы, которые объявлены в main, они висят в стеке, потом при вызове какой-нибудь функции, создаются еще локальные - и забивают в этот момент стек. я вижу размер стека, вижу, что заполнен, но как же быть, если массив нужен? Объявить большой глобальный - да, но если не хватит памяти, отведенной под глобальные переменные? Кстати, в руководстве, которое вы часто поминаете, сказано, что при начальной инициализации, нужно инициализировать указатель стека(указав вершину ОЗУ). Как записывается инициализация SP?
Сообщение отредактировал Daria - Oct 7 2008, 18:21
|
|
|
|
|
Oct 7 2008, 19:07
|
Гуру
     
Группа: Свой
Сообщений: 10 920
Регистрация: 5-04-05
Пользователь №: 3 882

|
Цитата(Daria @ Oct 8 2008, 00:12)  Все же пока не ясно  Есть массивы, которые объявлены в main, они висят в стеке, потом при вызове какой-нибудь функции, создаются еще локальные - и забивают в этот момент стек. Пример из программы можете привести, где по-вашему создаются локальные копии массивов локальных переменных? Цитата(Daria @ Oct 8 2008, 00:12)  я вижу размер стека, вижу, что заполнен, но как же быть, если массив нужен? Объявить большой глобальный - да, но если не хватит памяти, отведенной под глобальные переменные? Либо вы не совсем аккуратно программируете, либо выбранный кристалл (размер имеющегося у него ОЗУ) не соответствует вашей задаче. Цитата(Daria @ Oct 8 2008, 00:12)  Кстати, в руководстве, которое вы часто поминаете, сказано, что при начальной инициализации, нужно инициализировать указатель стека(указав вершину ОЗУ). Как записывается инициализация SP?  Поскольку вы программируете на Си, а не на ассемблере, то задачей инициализации указателя стека и очисткой/инициализацией глобальных и статических переменных, находящихся в сегменте DATA_Z, занимается сам компилятор. Все это делается в процедуре StartUp, которая выполняется ДО вызова функции main. Надеюсь вы в курсе, что main это тоже функция и у нее есть пролог и эпилог, как и у подавляющего большинства других функций? Вот если бы вы писали программу полностью на ASM, то инициализацию указателя стека и инициализацию/очистку глобальных переменных пришлось бы писать тоже вам. Вручную
|
|
|
|
|
Oct 10 2008, 20:23
|
Местный
  
Группа: Участник
Сообщений: 229
Регистрация: 24-02-08
Пользователь №: 35 345

|
Цитата(rezident @ Oct 7 2008, 23:07)  Либо вы не совсем аккуратно программируете, либо выбранный кристалл (размер имеющегося у него ОЗУ) не соответствует вашей задаче. Однозначно, первое  ОЗУ = 2 КВ. Но я поняла, в чем дело - это не стек забивался, а размер stack heap был по умолчанию задан 50 байт.  Я не тормоз, я медленный газ  Но теперь все вроде в порядке.
|
|
|
|
2 чел. читают эту тему (гостей: 2, скрытых пользователей: 0)
Пользователей: 0
|
|
|