PROGCONT.RU

Форма входа







Регистрация Вход/

STM8S индикация реальной скорости вращения двигателя на модуле TM1638 или самодельный тахометр.

Ссылка на видео в YouTube Эта статья дополнение к статье управление коллекторным двигателем, там мы просто управляли вращением, тут мы будем управлять реальной скоростью которую будем выводить на модуль TM1638.
 Ниже как у меня реализован контроль скорости, который состоит из двух основных элементов: оптический прерыватель и диска из бумаги с восемью прорезями.

нет рисунка

 Общая схема ниже, оптический прерыватель RPI-574 ROHM был изъят из платы старого принтера, вы можете подобрать любой другой, работает прерыватель как транзистор, проходит свет значит открыт, нет закрыт.

нет рисунка

 Про программу ШИМ управление уже описано в этой статье ссылка, как устроен тахометр читаем ниже.
 Принцип работы простой, диск крутится с ротором открывая и закрывая оптический прерыватель с помощью прорезей, остается только сочетать все эти открытия или закрытия за одну секунду, что и будет делать наш контроллер STM8S003F3P6.
 Тахометр(счетчик оборотов) реализован следующим образом, вход TIM1, TIM1_CH3(PC3) настроен как Input capture(захват) от которого будет срабатывать прерывание при переходе с низкого к высокому уровню(диск закрывает щель прерывателя), в этом прерывании мы просто прибавляем счетчик срабатывания прерывателя. Сам TIM1 настроен для отсчета одной секунды после отсчета которой должно срабатывать другое прерывание, в котором будет вычисление скорости вращения, запись ее в переменную для отображения и сброс счетчика срабатывания прерывателя. Как вы наверное поняли, показания индикатора обновляется каждую секунду.
 Сразу скажу что для подсчета количества срабатывания оптического прерывателя можно было реализовать через интерфейс энкодера который имеется у всех контроллеров STM8, но те ноги на которых можно было это сделать заняты SPI для связи с модулем TM1638.
 Тут описание всех настроек TIM1 для работы тахометра, начнем с самого таймера.

TIM1_TimeBaseInit( 1999, TIM1_COUNTERMODE_UP, 1000, 0);

 Пред делитель будет 1999, TIM1_COUNTERMODE_UP счет только вверх, считать до 1000, частота тактирования 2MHz, полный счет равен 2000000/((1999+1)*1000)=1 секунда.
 Настройка вывода и таймера в режим захвата будет так.

GPIO_Init(GPIOC,GPIO_PIN_3,GPIO_MODE_IN_PU_NO_IT);
TIM1_ICInit( TIM1_CHANNEL_3, TIM1_ICPOLARITY_RISING, TIM1_ICSELECTION_DIRECTTI, TIM1_ICPSC_DIV1, 0);

 Настраиваем вывод GPIOC PIN_3 как вход с подтяжкой к питанию, без прерывания. Потом настраиваем TIM1_CHANNEL_3,TIM1_ICPOLARITY_RISING что бы он срабатывал толь при переходе с низкого к высокому уровню, TIM1_ICSELECTION_DIRECTTI прямой ход к IC3, TIM1_ICPSC_DIV1 пред делитель входа равен 1 и фильтр не работает последний нуль.
 Осталось сбросить флаги прерываний TIM1_ClearFlag(TIM1_FLAG_UPDATE) и ClearFlag(TIM1_FLAG_CC3), настроить прерывания от переполнения таймера TIM1_ITConfig( TIM1_IT_UPDATE, ENABLE) и TIM1_ITConfig( TIM1_IT_CC3, ENABLE) от входа TIM1_CHANNEL_3(PC3), TIM1_Cmd(ENABLE) включить таймер и прерывания enableInterrupts().

TIM1_ClearFlag(TIM1_FLAG_UPDATE);
TIM1_ITConfig( TIM1_IT_UPDATE, ENABLE);
TIM1_ClearFlag(TIM1_FLAG_CC3);
TIM1_ITConfig( TIM1_IT_CC3, ENABLE);
TIM1_Cmd(ENABLE);
enableInterrupts();

 В обработчиках прерываний INTERRUPT_HANDLER( TIM1_UPD_OVF_TRG_BRK_IRQHandler, 11) и INTERRUPT_HANDLER( TIM1_CAP_COM_IRQHandler, 12) происходит все действие тахометра, в первом вычисляем, сохраняем результат и сбрасываем счетчик количества подсчитанных прорезей, во втором считаем прорези диска.
 Как вы видите для работы тахометра нужно не так много усилий и кода, большую часть занимает индикация скорости и управление, ниже я как обычно выложил всю программу.

#include "stm8s.h"
void send_command(uint8_t com);
void send_data(uint8_t *send_data_display);
void display_data(uint32_t multiplier);
uint8_t scan_button(void);
void pause(uint32_t p);
uint8_t result=0;
uint32_t pwm_moto=0;
uint32_t speed_moto=0;
uint32_t speed_moto_ind=0;
//Массив для отправки данных
uint8_t send_data_display[16];
//Массив наших символы для индикации 1,2,3 и так далее
uint8_t symbol[10]={63,6,91,79,102,109,125,7,127,111};
int main( void ){
//Настраиваем порты для SPI
GPIO_Init(GPIOC, GPIO_PIN_5, GPIO_MODE_OUT_OD_HIZ_FAST);
GPIO_Init(GPIOC, GPIO_PIN_4, GPIO_MODE_OUT_OD_HIZ_FAST);
SPI_Init( SPI_FIRSTBIT_LSB, SPI_BAUDRATEPRESCALER_16, SPI_MODE_MASTER, SPI_CLOCKPOLARITY_LOW, SPI_CLOCKPHASE_1EDGE, SPI_DATADIRECTION_1LINE_TX, SPI_NSS_SOFT, 0x07); SPI_Cmd(ENABLE);
//Настраиваем вывод PA3, TIM2 для генерации ШИМ и включаем
GPIO_Init( GPIOA, GPIO_PIN_3, GPIO_MODE_OUT_OD_HIZ_FAST);
TIM2_TimeBaseInit(TIM2_PRESCALER_1, 100);
TIM2_OC3Init(TIM2_OCMODE_PWM1, TIM2_OUTPUTSTATE_ENABLE, 0, TIM2_OCPOLARITY_LOW);
TIM2_Cmd(ENABLE);
//Настраиваем TIM1 для отсчета одной секунды
TIM1_TimeBaseInit( 1999, TIM1_COUNTERMODE_UP, 1000, 0);
GPIO_Init(GPIOC,GPIO_PIN_3, GPIO_MODE_IN_PU_NO_IT);
TIM1_ICInit( TIM1_CHANNEL_3, TIM1_ICPOLARITY_RISING, TIM1_ICSELECTION_DIRECTTI, TIM1_ICPSC_DIV1, 0);
TIM1_ClearFlag(TIM1_FLAG_UPDATE);
TIM1_ITConfig( TIM1_IT_UPDATE, ENABLE);
TIM1_ClearFlag(TIM1_FLAG_CC3);
TIM1_ITConfig( TIM1_IT_CC3, ENABLE);
TIM1_Cmd(ENABLE);
enableInterrupts();
 while(1){
//Включаем модуль и задаем автоматический инкремент адреса для данных
send_command(140);
send_command(64);
//Отображаем новое значение
display_data(speed_moto_ind);
pause(8000);
//Опрашиваем модуль на нажатие кнопок и присваиваем значение result
result=scan_button();
pause(100);
//Защита от ложных срабатываний, проверяем еще раз нажатие кнопок
if(result!=scan_button()){result=0;}
//Если нажата кнопка, тогда определяем какая и выберем действие
   if(result!=0){
//Если кнопка 1 импульс(pwm_moto) равен нулю, значит мотор не работает
      if(result==1){pwm_moto=0;}
//Если кнопка 2 то увеличиваем время импульса но не больше 100
      if(result==2){if(pwm_moto<100){pwm_moto++;}}
//Если кнопка 3 уменьшаем импульс но не меньше 0
      if(result==4){if(pwm_moto>0){pwm_moto--;}}
//Следующие кнопки выбирают значения импульса 20, 40, 60, 80 и 100
      if(result==8){pwm_moto=20;}
      if(result==16){pwm_moto=40;}
      if(result==32){pwm_moto=60;}
      if(result==64){pwm_moto=80;}
      if(result==128){pwm_moto=100;}
//Устанавливаем новое значение импульса
      TIM2_SetCompare3(pwm_moto);
   }
 }
}
//Этот обработчик будет срабатывать каждую секунду
INTERRUPT_HANDLER(TIM1_UPD_OVF_TRG_BRK_IRQHandler, 11)
{
   speed_moto_ind=(speed_moto*125/100);
   speed_moto=0;
   TIM1_ClearFlag(TIM1_FLAG_UPDATE);
}
//Этот обработчик будет срабатывать на каждой прорези диска
INTERRUPT_HANDLER(TIM1_CAP_COM_IRQHandler, 12)
{
    speed_moto++;
   TIM1_ClearFlag(TIM1_FLAG_CC3);
}
//Функция для отправки команды
void send_command(uint8_t com){
   GPIO_WriteLow(GPIOC, GPIO_PIN_4);
   SPI_SendData(com);
   while( (SPI->SR & SPI_FLAG_BSY) );
   GPIO_WriteHigh(GPIOC, GPIO_PIN_4);
}
//Функция для опроса кнопок
uint8_t scan_button(void){
   uint8_t scan_result;
    GPIO_WriteLow(GPIOC, GPIO_PIN_4);
    SPI_SendData(66);
   while( (SPI->SR & SPI_FLAG_BSY) );
//Перенастраиваем SPI на прием данных, принимаем 4байта и сокращаем их до 1байта
   SPI_BiDirectionalLineConfig(SPI_DIRECTION_RX);
    while( !(SPI->SR & SPI_FLAG_RXNE) );
   scan_result=SPI->DR;
   while( !(SPI->SR & SPI_FLAG_RXNE) );
   scan_result|=(SPI->DR<<1);
   while( !(SPI->SR & SPI_FLAG_RXNE) );
   scan_result|=(SPI->DR<<2);
   while( !(SPI->SR & SPI_FLAG_RXNE) );
    scan_result|=(SPI->DR<<3);
    GPIO_WriteHigh(GPIOC, GPIO_PIN_4);
//Перенастраиваем SPI на передачу данных
   SPI_BiDirectionalLineConfig(SPI_DIRECTION_TX);
   return scan_result;
}
//Функция для отправки данных
void send_data(uint8_t *send_data_display){
    GPIO_WriteLow(GPIOC, GPIO_PIN_4);
    SPI_SendData(192);
   while( (SPI->SR & SPI_FLAG_BSY) );
   for( uint8_t i=0;i<16;i++){
       SPI_SendData(send_data_display[i]);
      while( !(SPI->SR & SPI_FLAG_TXE) );
   }
   while( (SPI->SR & SPI_FLAG_BSY) );
   GPIO_WriteHigh(GPIOC, GPIO_PIN_4);
}
//Вычисляем значения и заполняем ими массив для отправки в модуль для отображения
void display_data(uint32_t multiplier){
//Раскомментируйте то что вам нужно для отображения
   //send_data_display[0]= symbol[multiplier/10000000];
   multiplier= multiplier-(multiplier/10000000*10000000);
    //send_data_display[2]= symbol[multiplier/1000000];
   multiplier=multiplier-(multiplier/1000000*1000000);
   //send_data_display[4]= symbol[multiplier/100000];
   multiplier= multiplier-(multiplier/100000*100000);
    //send_data_display[6]= symbol[multiplier/10000];
   multiplier= multiplier-(multiplier/10000*10000);
    //send_data_display[8]= symbol[multiplier/1000];
   multiplier= multiplier-(multiplier/1000*1000);
   send_data_display[10]= symbol[multiplier/100];
    multiplier= (multiplier-multiplier/100*100);
//Тут добовляем точку
   send_data_display[12]= symbol[multiplier/10]|128;
    multiplier= multiplier-(multiplier/10*10);
   send_data_display[14]= symbol[multiplier];
    send_data(send_data_display);
}
void pause(uint32_t p){
   for(uint32_t i=0; i<p; i++){}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t* file, uint32_t line)
{
while (1){}
}
#endif

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


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