Jump to content

    

MSP430 - вопросы от чайника

1. если вы хотите задействовать аппаратные возможности таймера по формированию импульсов, то сразу же используйте и аппаратную возможность синхронизации запуска АЦП. Обратите внимание, что вместо того, чтобы программно "дергать" битом ASC12SC можно аппаратно управлять запуском преобразования от сигналов TA1 или TB0, TB1. Т.е. как я и предлагал ранее - формировать времянку с помощью выходного сигнала TA1 и им же управлять запуском АЦП.

Спасибо, постараюсь разобраться :)

2. Насчет шумов АЦП и осреднения. Время сэмплирования и преобразования нужно выбирать, исходя в т.ч. из соображений сопротивления источника сигнала. Вы не слишком ли малое время сэмплирования выбрали? Поскольку у вас количество каналов измерения значительно меньше, чем количество каналов АЦП, то усреднение можно сделать полуаппаратно. Запускаете АЦП в режиме последовательности каналов которые сконфигурированы на один и тот же вход АЦП. А затем простым вычислением среднего арифметического значения нескольких значений ADC12MEMx усредняете.

А я вот примерно так и собиралась сделать :) даже и не знаю, как по-другому :biggrin: спасибо еще раз.

Господа, товарищи, и все же

Помогите разобраться с переопределением типов!!!

int x,y; переопределяю (float) (х), возвращаю return (int)(x/y), так как пока хочу работать с целыми, и принимающая программка сделана под них. Но отношение получается либо 0, либо 1. В чем тут дело? Объясните, пожалуйста!

заранее спасибо.

Share this post


Link to post
Share on other sites
Помогите разобраться с переопределением типов!!!

int x,y; переопределяю (float) (х), возвращаю return (int)(x/y), так как пока хочу работать с целыми, и принимающая программка сделана под них. Но отношение получается либо 0, либо 1. В чем тут дело?

Я думаю, что дело в том, что x никогда не бывает больше 2y-1.

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

Share this post


Link to post
Share on other sites
Помогите разобраться с переопределением типов!!!

int x,y; переопределяю (float) (х), возвращаю return (int)(x/y), так как пока хочу работать с целыми, и принимающая программка сделана под них. Но отношение получается либо 0, либо 1. В чем тут дело? Объясните, пожалуйста!

Это не переопределение, а приведение типа. Чтобы операция деления производилась с числами именно типа float нужно приведение типов сделать непосредственно в выражении

return ((int)((float)x/(float)y))

причем int и второй float можно не указывать, т.к. в операции деления приведение типа к более "высокому" будет сделано автоматически. И результат операции будет приведен к типу int автоматически в соответствии с типом результата, возвращаемым функцией. Но для наглядности можно написать так, как я указал.

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

float fTmp;
if (y!=0)         //проверка исключения "деление на нуль"
{ fTmp=(float)x;  //приведение типа для наглядности
  fTmp/=(float)y; //опять же здесь приведение типа только для наглядности
}
else
  fTmp=0;        //вынужденная обработка возникшего исключения
return((int)fTmp);

Share this post


Link to post
Share on other sites

Большое спасибо, rezident, теперь все получилось:)))

Share this post


Link to post
Share on other sites

Я рад вашим успехам :)

Кстати, если вам требуется округление, то можно (до преобразования в int) при вычислениях с float прибавлять число 0.5f.

Share this post


Link to post
Share on other sites
прибавлять число 0.5f.

если число положительное, и вычитать 0.5, если число отрицательное.

Примерно, в общем виде, где-то так:

/* --- round() -------------------------------------------------------------------------------------------- **
*  На выходе получаем округлённый float-результат
*  value    - что округлять
*  accuracy - с какой точностью округлять (например 2. или 0.03 или 0.1 и т.д.)
* -------------------------------------------------------------------------------------------------------- */
float round( float value, float accuracy )
{
  return( ((long)( value / accuracy + ( value < (float)0. ? (float)-0.5 : (float)0.5 ))) * accuracy );
}

Share this post


Link to post
Share on other sites
Я рад вашим успехам :)

Зря иронизируете, я медленно(очень медленно), но верно расту :biggrin:

Кстати, если вам требуется округление, то можно (до преобразования в int) при вычислениях с float прибавлять число 0.5f.

Спасибо за совет - пригодится :)

 

 

 

если число положительное, и вычитать 0.5, если число отрицательное.

Примерно, в общем виде, где-то так...

Спасибо, VAI.

Share this post


Link to post
Share on other sites
Зря иронизируете, я медленно(очень медленно), но верно расту :biggrin:
Никакой иронии! Это чистосердечно. Смайлик неподходящий выбрал. Вот такие надо было. thumbsup.gif :a14:

Share this post


Link to post
Share on other sites

Здравствуйте, и снова вопросы :) много, много вопросов :)

1.!! Сначала по поводу усреднения отсчетов для уменьшения шума -

предлагали использовать бит MSC в регистре ADC12CTL0. Правильно ли я понимаю, что тогда значения выборок будут постоянно(до сброса бита ENC) записываться в регистры ADC12MEMx, и получить усредненное значение можно считывая значения из разных регистров ADC12MEMx и деля на их количество? Но у меня 16 каналов АЦП, задействовано в дальнейшем будет 5. Т.е. я могу использовать для усреднения только три значения из регистров ADC12MEMx - маловато будет... :(

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

Делаю усреднение пока так(знаю, что криво, но)

int filter(int r[10])// среднее арифметическое 10 значений

{

int s, t;

s = 0;

for(t = 0 ; t < 10 ; t++)

s+=r[t];

return s/10;

}

...

ADC12CTL0 = ADC12ON + REFON + SHT0_8;// настройка АЦП

ADC12CTL1 = CSTARTADD0 + CSTARTADD1 + ADC12SSEL_1+ CONSEQ_1+ SHS_1;

ADC12MCTL3 = INCH_3;

ADC12MCTL4 = INCH_4 + EOS;

index = 0;

...

И, собственно

if (TACCTL0 & CCIFG)// если таймер переключился

{

 

if (P1OUT & BIT5)// и, если вывод все еще в единице,т.е. еще идет длинный импулсьс

{

 

ADC12CTL0 |= ENC;

Vr[0][index] = ADC12MEM3; // записываем в массив значения из ADC12MEMх

Vr[1][index] = ADC12MEM4;

ADC12CTL0 &= ENC;

 

index++;

if (index >= 10)// когда значений в массиве 10

{

index = 0;

offset[0] = (Vs[0] - filter(Vr[0]))/2; // Вычисление смещений уровня сигнала

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

 

Vx = filter(Vr[0]) - offset[0]; //вычисление координат вектора маг.индукции

Vy = filter(Vr[1]) - offset[1];

 

azimut = calculation (Vx,Vy); //вычисление азимута

 

}

 

 

send_int(azimut);

 

} send_int(azimut) - вычисленный азимут посылается на СOM. Описание send_int было выше в теме, да и не в ней суть, на вычисления тоже можно не обращать внимания :)

ВОПРОС! -

2.!! В данном случае вычисляю азимут 10 раз, а посылаю только один. т.е. 10 раз посылается одно и то же значение. Если переношу строчку send_int(azimut); под строку

azimut = calculation (Vx,Vy); - т.е. сразу после вычисления и отправляю, то программа не работает :( На СOM ничего не приходит! :07: Поясните, пожалуйста, если будет время разобраться - ПОЧЕМУ?!

3.!! Вопрос не по теме MSP430, уж извините, но вдруг кто-нибудь знает - Когда считываю уровень сигнала с датчика, он самопроизвольно "плывет", плата лежит неподвижно, а уровень за минуту убегает довольно прилично, при вычислении азимута получается, что в минуту градусов +/- 5 :07: С чем это может быть связано? Датчик, напоминаю, HMC1002 :biggrin: Вопрос в основном к Курту, если он заглянет. Да, Kurt, отправила Вам письмо - загляните в ящик - там еще куча вопросов по компасу конкретно :biggrin: Ну, Вы сами обещали помочь :biggrin:

Ну вот, пока все - помогите, кто может чем сможет :biggrin: Не ругайте за безграмотность и всех

с победой сбороной России :biggrin: :yeah:

Share this post


Link to post
Share on other sites
ну, или я все же не понимаю, поясните тогда принцип использования бита MSC.
Бит MSC не имеет прямого отношения к усреднению. Он лишь позволяет автоматически запускать таймер сэмплирования (Sample Timer) после каждого преобразования (если выбран режим последовательности каналов). На блок-схеме из User's Guide хорошо видно, что устройство выборки-хранения (Sample-and-Hold или УВХ в отечественной терминологии), подключенное к входному 16-и канальному мультиплексору управляется сигналом SAMPCON. Лог.1. SAMPCON включает режим выборки, в котором конденсатор УВХ заряжается до напряжения входного сигнала, подключенного к выбранному входу мультиплексора, а лог.0 отключает УВХ от мультиплексора и запускает преобразование. Длительность лог.1 задается таймером сэмплирования, либо внешним сигналом в обход таймера сэмплирования. Сам таймер сэмплирования запускается по фронту сигнала SHI, который формируется либо программно от ADC12SC, либо одним из трех внешних сигналов от модулей таймера A или таймера B. Так вот установка бита MSC позволяет не ждать фронта сигнала SHI для запуска каждого последующего преобразования, а формировать его аппаратно по окончании предыдущего преобразования. Но это все работает только, если выбран режим управления от Sample Timer и режим последовательности каналов.

Делаю усреднение пока так(знаю, что криво, но)

int filter(int r[10])// среднее арифметическое 10 значений

{

int s, t;

s = 0;

for(t = 0 ; t < 10 ; t++)

s+=r[t];

return s/10;

}

Ну некоторая "кривизна" присутствует только при передаче аргумента функции. Передавать следует указатель на массив отсчетов и возможно еще и длину этого массива.

int filter(int *ptr, int size)
{ int s=0, t;
  for(t=0; t<size; t++)
  s+=*ptr;
  ptr++;
  if (size>0)  //проверим на исключение "деление на нуль"
    return (s/size);
  else
    return 0;
}

Вызов функции будет такой

x=filter(r, 10);

или

x=filter(&r[0], 10);

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

...

ADC12CTL0 = ADC12ON + REFON + SHT0_8;// настройка АЦП

ADC12CTL1 = CSTARTADD0 + CSTARTADD1 + ADC12SSEL_1+ CONSEQ_1+ SHS_1;

ADC12MCTL3 = INCH_3;

ADC12MCTL4 = INCH_4 + EOS;

index = 0;

...

Еще раз обращаю внимание, что во избежание ошибок при формировании слова из отдельных битов следует пользоваться побитовым ИЛИ '|', а не оператором сложения '+'.

И, собственно

 

if (P1OUT & BIT5)// и, если вывод все еще в единице,т.е. еще идет длинный импулсьс

{

Здесь возможно имеется потенциальная алгоритмическая ошибка. Проверять нужно не только до начала преобразования, но и после окончания преобразования. И если после окончания преобразования бит сменил свое состояние, то результат последнего преобразования нужно отбрасывать. Следовательно у вас может быть не 10 отсчетов, а меньше. Поэтому выше я предложил передавать в функцию фильтрации еще и реальную длину буфера отсчетов.

2.!! В данном случае вычисляю азимут 10 раз, а посылаю только один. т.е. 10 раз посылается одно и то же значение. Если переношу строчку send_int(azimut); под строку

azimut = calculation (Vx,Vy); - т.е. сразу после вычисления и отправляю, то программа не работает :( На СOM ничего не приходит! :07: Поясните, пожалуйста, если будет время разобраться - ПОЧЕМУ?!

Затрудняюсь сходу дать однозначный ответ, но думаю, что причиной является факт, что получение значения измерения и передача его через UART это процессы вообще-то асинхронные, но вы их пытаетесь принудительно синхронизировать без использования буферов. Вы используете передачу по опросу бита готовности, а не по прерыванию. А каждая передача "тормозит" измерения на время передачи двух символов. Поэтому за тот же самый интервал времени (который у вас фиксированный и определяется таймером) вы получаете меньше отсчетов, чем предполагаемое вами количество (10). Следовательно условие if (index >= 10) не выполняется, результата azimut вы не получаете, и передача через UART тоже не идет. Для решения этой проблемы рекомендую использовать передачу по прерываниям и формировать для передачи свой собственный буфер, линейный или циклический.

 

По 3-му вопросу ответить не могу. Я не "копенгаген", да и с подобными датчиками я не работал :laughing:

post-3882-1214160020_thumb.jpg

Share this post


Link to post
Share on other sites

Здравствуйте снова.

Rezident, спасибо за подробный разбор, но вот я начала после Ваших комментов разбираться и окончательно зависла :07: :biggrin:

Во-первых, после того, как фильтр сделала по Вашему совету - с делением не на 10, а на index. числа начали получаться жуткими и бессмысленными, вразнобой и без всякой связи с поворотом платы - как это можно объяснить? :07:

Во-вторых, я, наивная, полагала, что преобразование АЦП запускается сейчас синхронно с переключением таймера, я ведь вроде не битом ADC12SC "дергаю", а ставлю бит SHS_1 в ADC12CTL1 - т.е использую выход таймера А1... или нет? Проверка if (P1OUT & BIT5) идет не перед стартом преобразования, а перед тем, как записывать новый элемент в массив, т.е. как раз то, о чем Вы говорите... так мне казалось :)

В третьих, тут вроде все советовали как раз использовать как можно меньше прерываний, лучше вообще без них, вот и стараюсь без них :)

и вообще - Вы обещали рассказать, как в данном случае не программно дергать P1OUT, а использовать выход таймера - как это можно сделать, ведь изменение состояния вывода должно произойти только через 918 переключений таймера :05:

И все-таки вопрос - почему send_int(azimut) нельзя перенести в то место, в которое хочется :) ? Если бы условие if (index >= 10) никогда не выполнялось, то и вычисление никогда не происходило бы, а что-то таки вычисляется. Без фильтра работает :) только сильно скачет, что не есть хорошо.

Вот такие непонятки :)

будет время, ответьте, пожалуйста.

Edited by Daria

Share this post


Link to post
Share on other sites

Daria, давайте все исходники (весь проект). Варианты возможных нюансов в многоветвистое дерево вырастают, а телепатия что-то тяжело нынче идет :)

Я бы вам посоветовал отладить работу АЦП не с датчиком, а с постоянными напряжениями для начала. Вы еще не до конца разобрались с синхронной работой ADC12 и TimerA и вообще с функционированием ADC12.

Share this post


Link to post
Share on other sites
Я бы вам посоветовал отладить работу АЦП не с датчиком, а с постоянными напряжениями для начала.

да пробовала :) Вроде нормально идет. Правда, пробовала без всяких усреднений...

Вы еще не до конца разобрались с синхронной работой ADC12 и TimerA и вообще с

функционированием ADC12.

Эт точно :biggrin: да и с USART похоже тоже. Видимо все дело в том, как я отправляю число. Функция send_int получилась кривоватая. Вот подскажите, как просто и хорошо отправить целое число, 12бит, чтобы занять как можно меньше времени и получить как можно меньше ошибок при приеме? Пробовала без флажка 0xFF, означающего конец посылки, но при "слеплении" получаются ошибки.

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

А проект пока маленький совсем, прикладываю текст.

Не ругайтесь на "+", я потом обязательно исправлю, все как-то недосуг :)

Вообще спасибо за поддержку:)

1.txt

Edited by Daria

Share this post


Link to post
Share on other sites
Функция send_int получилась кривоватая. Вот подскажите, как просто и хорошо отправить целое число, 12бит, чтобы занять как можно меньше времени и получить как можно меньше ошибок при приеме?

 

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

Пример:

адрес получателя 1 или 2 байта

идентификатор пакета 2 байта - соответвует идентификатору запроса.

длина данных пакета 1 или 2байт

данные []

адрес отправителя 1 или 2 байта

СRC 2 байт

 

истина прописная, протокол то все равно прийдется организовывать )))

либо воспользоватся общепринятыи протоколом для Вашей конторы ...

Share this post


Link to post
Share on other sites
Имеет смысл сразу организовать протокол обмена.

либо воспользоватся общепринятыи протоколом для Вашей конторы ...

Спасибо. Общепринятого протокола нет - контора большая, кто обменивается, тот между собой обычно и договаривается:) А пока как-нибудь. А имеет смысл писать такой протокол ради того, чтобы просто постоянно отправлять число от 0 до 360 на COM- и больше ничего?

Да, rezident, бог с ним. с усреднением - все равно особенно не поможет, нужно делать норамльный цифровой фильтр. А я до него еще не доросла :) Чутка попозже. Пока вот вопрос - записываю число во flash, использую режим записи по словам. т.е. int число пишется легко и без проблем. А как записать float - надо уже режим поблоковой записи? Или нет?

Edited by Daria

Share this post


Link to post
Share on other sites
Guest
This topic is now closed to further replies.
Sign in to follow this