STM32 I/O модуль на MODBUS с PWM выходами (Апгрейд2)

Продолжаем апгрейд нашего I/O модуля всякими кунштюками в предыдущей статье we.easyelectronics.ru/STM32/stm32-i-o-modul-na-modbus-i-nemnogo-acp-prevraschenie-bryuk.html было рассказано как прикрутить АЦП и термисторы в этой мы прикрутим PWM -т.е. по русски широтно импульсную модуляцию на то, что раньще у нас было дискретными выходами и сможем чем нито управлять аналогово…
Видео в качестве анонса:

Для чего нужен PWM? он нужен для того чтобы управлять чем то при помощи аналогового сигнала который может быть чисто PWM — например для управления яркостью светодиода, либо через интегратор (резистор с конденсатором ) преобразуя в напряжение от 0 до напряжения питания у нас 3.3В для duty цикла от 0 до Максимума равного разрешению таймера соответсвенно.

Причем делается это все плавно с достаточной точность (в примере 15бит), но чем больше бит тем меньше частота т.е. для того чтобы таймер дощелкал до 65535 ему вобщемто надо столько циклов при 24МГц (STM32F100C8T6) — он будет перезаряжаться 366 раз т.е. это будет частота PWM — 366Hz. Обычно эту частоту берут повыше с уменьшением точности (дабы не вешать огромных емкостей в интеграторе) раза в два больше нам будет как раз 732Hz нормально да и 15 бит нехилое разрешение. Во множестве устройств автоматики (особо точные применения не берем) разрядность аналоговых выходов гораздо меньше 8-10 бит т.к. большинство исполнительных устройств типа клапанов из за люфтов и всяких косяков не могут обеспечить даже такую реальную точность. Нус к делу.
Настройка выходов для подцепляния их таймером заключается в объявлении этих пинов выходами и использовании альтернативной функции, ну и как обычно не забываем затактировать нужный порт.

//pa8-11  выходы PWM
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
       GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_8|GPIO_Pin_9|GPIO_Pin_10|GPIO_Pin_11;
       GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
       GPIO_InitStructure.GPIO_Mode  =GPIO_Mode_AF_PP;//альтернативная функция выход пуш-пул
       GPIO_Init(GPIOA, &GPIO_InitStructure);


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


void Setup_PWM(void)
{
	TIM_OCInitTypeDef  TIM_OCInitStructure;
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;

	  RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);

	   /* Time Base configuration */
	  TIM_TimeBaseStructure.TIM_Prescaler = 0;
	  TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
	  TIM_TimeBaseStructure.TIM_Period = 32768;//максисмум таймера
	  TIM_TimeBaseStructure.TIM_ClockDivision = 0;
	  TIM_TimeBaseStructure.TIM_RepetitionCounter = 0;

	  TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);
	  TIM_ARRPreloadConfig  ( TIM1,ENABLE);

	  /* Channel 1, 2,3 and 4 Configuration in PWM mode */
	  TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2;//Обратная полярность TIM_OCMode_PWM1 -прямая 
	  TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
	  TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;//это для парных выходов
	  TIM_OCInitStructure.TIM_Pulse = 0;//Значение мощности для первого канала по умолчанию 0
	  TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low; //Еще раз обратная полярность :) 
	  TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;//полярность для N выходов
	  TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;//что делать в момент ожидания
	  TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;//для N выходов

	  TIM_OC1Init(TIM1, &TIM_OCInitStructure);
          
          //все значения структуры у нас одинаковые для всех каналов   
	  TIM_OCInitStructure.TIM_Pulse = 0;//Значение для второго канала
	  TIM_OC2Init(TIM1, &TIM_OCInitStructure);

	  TIM_OCInitStructure.TIM_Pulse = 0;//Значение для третьего канала
	  TIM_OC3Init(TIM1, &TIM_OCInitStructure);

	  TIM_OCInitStructure.TIM_Pulse = 0;//Значение для четвертого канала
	  TIM_OC4Init(TIM1, &TIM_OCInitStructure);

	  /* TIM1 counter enable */
	  TIM_Cmd(TIM1, ENABLE); //Пуск таймера

	  /* Main Output Enable */
	  TIM_CtrlPWMOutputs(TIM1, ENABLE); //Пуск PWM выходов

}

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

         TIM1->CCR1 = res_table.regs[5];
    	 TIM1->CCR2 = res_table.regs[6];
    	 TIM1->CCR3 = res_table.regs[7];
    	 TIM1->CCR4 = res_table.regs[8];

Проект для CooCox IDE приложен. Вроде все.

  • 0
  • 05 сентября 2011, 21:28
  • GYUR22
  • 1
Файлы в топике: io_analog_pwm_lt.zip

Комментарии (0)

RSS свернуть / развернуть
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.