Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Замечал ли кто-то странности с PWM на lpc2103 ?
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > ARM
avva
Уважаемый ALL, может быть кто-то сталкивался, помогите в какую сторону копнуть...

IAR 5.11,
IAR Elf Linker for ARM 5.11.0.50622 (5.11.0.50622)
IAR C/C++ Compiler for ARM 5.11.0.20622 (5.11.0.20622)


Вывожу с помощью PWM синус по 800 точкам. таблица синуса объявлена полуволной!!! из констант
unsigned int const sin1024[400]={\ ... }; константы в сегменте флэш-памяти lpc2103.
Задача вывода PWM тривиальная - по прерыванию 3-го канала (период) обновляю данные регистра сравнения T0MR0. после RC цепочки на ноге МАТ0.0 смотрим синус осциллографом.

Имею абсолютно непонятную ситуацию - "задир" (то есть T0MR0 имеет сильно неверное значение) (иногда два "задира", а иногда - ни одного) на синусе, причем этот задир только на одной (левой "верхней" то бишь значенияT0MR0 должны лежать в диапазоне [0 .. 1/2Period ] ) полуволне, и самое поразительное, что в зависимости от положения звёзд на рабочем проекте "задир(ы)" то проявляются, то не проявляется. На тестовом проекте задир всегда постоянен и всегда один.


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


Проблема проявляется даже на прилагаемом тестовом проекте (должен заработать у каждого под иар5.11 ). кварц 12мГц, обычная отладка от Олимекса с 2103.

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

ПРИЛАГАЕМЫЙ ПРОЕКТ Нажмите для просмотра прикрепленного файла
GetSmart
Могу сказать, что делал почти точно так же на LPC2132 и всё работало чудесно. Только перезагрузку MACH регистра делал по прерыванию 0-ого канала, который задаёт период PWM, то бишь обнуляет счётчик. В возникающем при этом прерывании обновлял PWMMR1 и стробировал PWMLER, а затем сразу же выходил из прерывания.

Кстати, таймер отличается от PWM тем, что у него нет защёлок на MACH регистрах (регистра PWMLER). Может в этом косяк.
Alex03
Както странно, у Вас PWM_period_val и период таймера (задающий частоту ШИМ) и амплитуда синуса....

Вот тут:
Код
  if (i>399){
// it seems problem in this part of
    val=( (PWM_period_val-( (PWM_period_val*usin[i-400] -1 )>>10))>>1 );
  }else{
    val=( ((PWM_period_val+((PWM_period_val*usin[i] -1 )>>10))>>1));  
  }

зачем "-1" ? Искажений добавляете? Впрочем возможно что в вашем случае они не появятся (при перермножении 375 на табличные значения наверняка не будет произведения где младшие 10 бит нулевые).
Может хотели ((PWM_period_val-1)*usin[i]) ?
Да и вообще весь этот код упростить можно.

И почему у Вас в таблице полуволны синуса первый элемент 3 а не 0? А если заменить на 0 то как раз та самая "-1" то и вылезет. smile.gif


Вот эта конструкция
Код
i = (i != 799) ? ++i : 0;
тоже достойна похвалы... sad.gif
avva
Цитата
Както странно, у Вас PWM_period_val и период таймера (задающий частоту ШИМ) и амплитуда синуса....
...
зачем "-1" ? Искажений добавляете? Впрочем возможно что в вашем случае они не появятся (при перермножении 375 на табличные значения наверняка не будет произведения где младшие 10 бит нулевые).
Может хотели ((PWM_period_val-1)*usin[i]) ?
Да и вообще весь этот код упростить можно.


Alex03, проблема абсолютно в другом.... (я согласен с вами, что "-1" это лишнее, но это всё-равно. таблица полу-синуса хоть с 10 может начинаться - она на 0х3FF отнормирована, так что там всё-равно.). проблема в том, что каким-то непонятным образом в tmrmr0 !однократно! попадает!? значение больше или равное периоду pwm, что вызывает установку соответсвтующей ноги в 0 на период шима. как такое происходит - ума не приложу.
пока все догадки и эксперименты были из области фантастики, по-этому я решил обратиться к многоуважаемому сообществу.

Цитата
И почему у Вас в таблице полуволны синуса первый элемент 3 а не 0? А если заменить на 0 то как раз та самая "-1" то и вылезет. smile.gif
Вот эта конструкция
Код
i = (i != 799) ? ++i : 0;
тоже достойна похвалы... sad.gif

это стандартная конструкция языка ANSI C, думаю это вопрос не принципиальный )


Цитата(GetSmart @ Jul 7 2008, 19:41) *
Могу сказать, что делал почти точно так же на LPC2132 и всё работало чудесно. Только перезагрузку MACH регистра делал по прерыванию 0-ого канала, который задаёт период PWM, то бишь обнуляет счётчик. В возникающем при этом прерывании обновлял PWMMR1 и стробировал PWMLER, а затем сразу же выходил из прерывания.

Кстати, таймер отличается от PWM тем, что у него нет защёлок на MACH регистрах (регистра PWMLER). Может в этом косяк.


я использую именно описанный в доке режим PWM, канал 3 - задаёт период PWM, а каналы 0 1 и 2 в рабочем проекте - разные PWM с одним периодом (канал 3)

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

Буду благодарен за умную мысль по теме.

ФОТО "ЗАДИРА"Нажмите для просмотра прикрепленного файла
GetSmart
Вобщем так. Без LER регистра будет наблюдаться следующий эффект. При возникновении прерывания (обнуления таймера) таймер начинает тикать с нуля со старым действующим MR (пока он не перезагрузится). В этот момент начинает выполняться обработчик. Дойдя до места в котором программа будет готова перезагрузить регистр MR, если при этом таймер ещё не дошёл до старого MR и уже прошёл новый MR, то MATCH-пин никогда не обнулится, т.к. он обнуляется по совпадению значений. Этот эффект возникает в местах, когда значение MR относительно мало, то есть недалеко от пика синусоиды. Ещё один косяк возникает с противоположной стороны этого же пика синусоиды - там происходит "выпадение" одного из значений на синусоиде, когда происходит "обгон" старых значений MR новыми.

Для облегчения жизни программистам в настоящем PWM ввели регистр PWMLER.
avva
Уважаемый GetSmart, похоже, что вы оказались правы или где-то очень очень рядом около того.

СПАСИБО.


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

100+ T0TCR = 0x01; //перезапуск таймера после окончания прерывания таймера

218 T0MCR = 0x0E00; // добавление STOP в процедуре инита периода шима таймера0 канал3
Alex03
Цитата(avva @ Jul 8 2008, 13:08) *
Код
i = (i != 799) ? ++i : 0;

это стандартная конструкция языка ANSI C, думаю это вопрос не принципиальный )

ИМХО это из области
Код
i += i++ + ++i;

Не рекомендовал бы так писать. К тому же если объявить i как volatile то переменная будет прописываться в памяти дважды (да и в некоторых других случаях, как то debug, тоже), хоть и одним значением.
Лучше/правильнее
Код
i = (i != 799) ? i+1 : 0;
или уж
Код
if(i != 799)
    ++i;
else
    i = 0;


Впрочем, ДА, к теме топика не имеет отношения.

Цитата(avva @ Jul 8 2008, 13:08) *
Понимаете, я бы понял, если бы проблема проявлялась на "пике" какого-то канала, тогда можно было бы говорить о переполнении и всякой такой простой ерунде, однако "задир" проявляется - фото

RC-цепочка двигает фазу, так что возможно что на фото и есть пик синусоиды...
avva
Alex03, спасибо, я учту в будущем. (серьёзно).

Цитата
RC-цепочка двигает фазу, так что возможно что на фото и есть пик синусоиды...


Фигню полную написали вы сейчас насчёт возможного пика.
GetSmart
Цитата(avva)
Уважаемый GetSmart, похоже, что вы оказались правы или где-то очень очень рядом около того.
Я оказался прав на 100% smile.gif
Только для обхода этого косяка не обязательно останавливать таймер. Достаточно уменьшить амплитуду синусоиды на несколько процентов чтобы в регистр MR не записывались очень маленькие значения. Например в диапазоне от 25 до 375. Минимально допустимое значение для MR зависит от скорости реакции на прерывание. Если у этого прерывания будет не самый высокий приоритет или будут возникать другие прерывания недопускающие вложенность, то будут глюки.
Alex03
Цитата(avva @ Jul 8 2008, 15:24) *
Alex03, спасибо, я учту в будущем. (серьёзно).

Так и я серьёзно, стоит ++ переставить и уже всё интересней smile.gif
Код
int i = 10;
i = (i != 799) ? i++ : 0;
MSVC 2003 - результат 10!


Цитата
Фигню полную написали вы сейчас насчёт возможного пика.
Я не знаю Ваших частот и номиналов, но чем больше R и С тем больше сдвиг фазы стремится к 90 градусов (с сильным уменьшением амплитуды). И тем правильнее форма сигнала.
Хотя судя по тому что "задир" такой "резкий" номиналы у Вас довольно небольшие.


Цитата(GetSmart @ Jul 8 2008, 15:36) *
...
Только для обхода этого косяка не обязательно останавливать таймер. Достаточно уменьшить амплитуду синусоиды на несколько процентов чтобы в регистр MR не записывались очень маленькие значения. Например в диапазоне от 25 до 375. Минимально допустимое значение для MR зависит от скорости реакции на прерывание. Если у этого прерывания будет не самый высокий приоритет или будут возникать другие прерывания недопускающие вложенность, то будут глюки.

+1

А скорость реакции увеличить легко выкинув полонстью
Код
__irq __arm void irq_handler (void)
{
void (*interrupt_function)();
unsigned int vector;

  vector = VICVectAddr;     // Get interrupt vector.
  interrupt_function = (void(*)())vector;
  if(interrupt_function != NULL){
    interrupt_function();  // Call vectored interrupt function.
  }
  
  VICVectAddr = 0;        // Clear interrupt in VIC.
  return;
}

Достаточно
Код
ldr pc, [pc, #-0xFF0]
в области векторов прерываний (по адресу 0x00000018)
И timer0_handler() объявить с __irq и с VICVectAddr = 0; в конце.
Зачем проверки, Вы сами себе не доверяете?

Ну и задержать можно всё на период
Код
__irq __arm void timer0_handler(void)
{
  static unsigned int i;
  static unsigned int val = ХХХ;

  T0MR0 = val;

  // Вычисление val для след. прерывания.


  VICVectAddr = 0;        // Clear interrupt in VIC.
}
cebotor
Цитата(avva @ Jul 8 2008, 13:16) *
100+ T0TCR = 0x01; //перезапуск таймера после окончания прерывания таймера

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

Вопрос - почему нельзя перегружать значение по соответствующему прерыванию (для нулевого канала по прерыванию матча нулевого канала )- будет все хорошо и вперед и назад ? smile.gif
avva
Цитата(GetSmart @ Jul 8 2008, 13:36) *
Я оказался прав на 100% smile.gif
Только для обхода этого косяка не обязательно останавливать таймер. Достаточно уменьшить амплитуду синусоиды на несколько процентов чтобы в регистр MR не записывались очень маленькие значения. Например в диапазоне от 25 до 375. Минимально допустимое значение для MR зависит от скорости реакции на прерывание. Если у этого прерывания будет не самый высокий приоритет или будут возникать другие прерывания недопускающие вложенность, то будут глюки.


Ок, GetSmart, Вы оказались правы на 100% a14.gif . К сожалению в данном случае мне нужен весь диапазон значений PWM и не удастся ввести постоянную корректировочную составляющую.
Данный проект синуса - это просто тестовый проект который был собран лишь для нахождения глюка.
Насчёт вложенных прерываний - да, придётся сделать вложенность по всей видимости.

Alex03, насчёт RC замнём - фильтруется несущая 40кГц для синуса 50Гц; насчёт ++ тоже в курсе biggrin.gif
У меня в нормальном проекте всего пока 5 прерываний крутится I2C, ADC, 2 периода ПВМ, один из которого системный синхронизующий и второй - достаточно медленный и ещё один таймера сравнения. В целом идей Ваш понятен и принят, насчёт проверок для дебилов - знаете, иногда когда "jmp $" не помогает, очень полезным оказывается писать все проверки и иногда вставлять даже в код NOP biggrin.gif .
alexander55
Цитата(GetSmart @ Jul 8 2008, 13:36) *
Я оказался прав на 100% smile.gif
Только для обхода этого косяка не обязательно останавливать таймер. Достаточно уменьшить амплитуду синусоиды на несколько процентов чтобы в регистр MR не записывались очень маленькие значения. Например в диапазоне от 25 до 375. Минимально допустимое значение для MR зависит от скорости реакции на прерывание. Если у этого прерывания будет не самый высокий приоритет или будут возникать другие прерывания недопускающие вложенность, то будут глюки.

Вы правы на 100%. Такие глюки я тоже проходил, но с какими-то другими uC.
Автора смутил факт наличия глюка не на самой вершине (из-за фазового сдвига, вносимого R-C цепочкой), а так бы он понял это сразу.
GetSmart
Цитата(cebotor @ Jul 8 2008, 16:26) *
Вопрос - почему нельзя перегружать значение по соответствующему прерыванию (для нулевого канала по прерыванию матча нулевого канала )- будет все хорошо и вперед и назад ? smile.gif
Вот это хорошая идея. Если при срабатывании MATCH-а вызывать прерывание, то можно сразу же перезагружать этот MR регистр. Этот алгоритм подходит когда в сигнале нет резких скачков, как раз для синусоиды. Однако нельзя будет загружать в MR значение большее или равное периоду. Таким образом за один период для трёхканального ШИМа будет три раза срабатывать одно и то же прерывание, в котором анализируя флаг TxIR можно узнать какой MR сработал.
Цитата(avva)
К сожалению в данном случае мне нужен весь диапазон значений PWM и не удастся ввести постоянную корректировочную составляющую.
Остановив таймер Вы её уже ввели smile.gif То есть после обнуления таймера появляются те же ~20 тактов паузы, только они теперь увеличили период с 375 до грубо 395 тактов PCLK. При этом получился плавающий период и нестабильная частота, особенно в присутствии других прерываний.
cebotor
Цитата(GetSmart @ Jul 8 2008, 14:56) *
Однако нельзя будет загружать в MR значение большее или равное периоду.

Cмысл этой фразы для меня остался тайной , по моему и так нельзя загружать в MR значение большее периода smile.gif
Цитата(GetSmart @ Jul 8 2008, 14:56) *
Таким образом за один период для трёхканального ШИМа будет три раза срабатывать одно и то же прерывание, в котором анализируя флаг TxIR можно узнать какой MR сработал.

я честно признаться не помню как в ЛПЦ работает ВИК , так что приходиться ли делить прерывание внутри или снаружи - это зависит от конкретного камня , а вот сама проблема , причину которой вы так удачно предположили - касается практически всех кристаллов в которых нету спец регистров типа прелоад значения smile.gif
GetSmart
Цитата(cebotor @ Jul 8 2008, 17:13) *
Cмысл этой фразы для меня остался тайной , по моему и так нельзя загружать в MR значение большее периода smile.gif
Если нужно чтобы на выходе MAT всегда было значение 1, то в регистр MR загружают значение большее или равное периоду таймера.
avva
Вариант 2, учитывающий абсолютно верное замечание CEBOTOR и замечание Alex03 насчёт запаздывания обновления на период и за счёт этого выигрыш в использовании предпосчитанного значения.

//InitTimer0():
215 T0MCR = 0x0601;

//
void timer0_handler(void)
{
static unsigned int i=0;
static unsigned int val=0;

if ( T0IR & 0x01 ) {
T0MR0 = val;
T0IR = (1<<0);
}

if ( T0IR & 0x08 ) {
if (i>399){
val=( (PWM_period_val-( (PWM_period_val*usin[i-400] )>>10))>>1 );
}else{
val=( ((PWM_period_val+((PWM_period_val*usin[i] )>>10))>>1));
}
i = (i != 799) ? i+1 : 0;
T0IR = (1<<3 );
}

return;
}



За сим предлагаю закончить с данной темой, содружеству спасибо! Не наступайте на такие грабли.
Lotor
Цитата(avva @ Jul 8 2008, 16:18) *
val=( ((PWM_period_val+((PWM_period_val*usin[i] )>>10))>>1));

Не хочется создавать новой темы, мне вот сейчас тоже надо получать синусоиду с помощью ШИМ. Не могли бы Вы пояснить, как формируете длину импульса с помощью этой формулы? Дело в том, что я думал использовать конструкцию аля val=PWM_period_val*usin[i]. У Вас же, получается val=(PWM_period_val+PWM_period_val*usin[i]/1024)/2. Не понятно...
avva
Цитата(Lotor @ Jul 9 2008, 13:13) *
Не хочется создавать новой темы, мне вот сейчас тоже надо получать синусоиду с помощью ШИМ. Не могли бы Вы пояснить, как формируете длину импульса с помощью этой формулы? Дело в том, что я думал использовать конструкцию аля val=PWM_period_val*usin[i]. У Вас же, получается val=(PWM_period_val+PWM_period_val*usin[i]/1024)/2. Не понятно...


внутри таблица синуса - половинка волны в unsigned c диапазоном значений 0-1024. по-этому нормировка амплитуды = 1/1024 ну а на 2 делится потому-что от середины половинка строится...
проект скачайте, поиграйтесь и всё поймёте..
Lotor
Цитата(avva @ Jul 9 2008, 13:27) *
внутри таблица синуса - половинка волны в unsigned c диапазоном значений 0-1024. по-этому нормировка амплитуды = 1/1024 ну а на 2 делится потому-что от середины половинка строится...
проект скачайте, поиграйтесь и всё поймёте..

Спасибо, разобрался.
Alex03
Если бы PWM_period_val было чётным, то можно было бы упростить до
Код
val= (PWM_period_val >> 1) + ((PWM_period_val*usin[i] )>>11);

(PWM_period_val >> 1) - константа (по крайней мере в нормальных компиляторах)

Иначе
Код
val= ((PWM_period_val << 10) + (PWM_period_val*usin[i]) )>>11;

(PWM_period_val << 10) - тоже константа поэтому один сдвиг экономится.

И я бы всётаки ввёл макрос PWM_AMPLITUDE
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.