roboforum.ru

Технический форум по робототехнике.

Управление сервой с помощью потенциометра. МК PIC16F887

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

boez » 09 июл 2010, 17:03

А там случайно таймер никак нельзя использовать? Пусть даже программно (в обработчике таймерного прерывания дергать ногой)? Все ж таки таймеры приятнее для генерации ШИМ, чем цикловые задержки...

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

Zeus » 09 июл 2010, 17:36

Можно сразу воспользоваться готовой библиотекой PWM, которая упрощает использование PWM HardWare модуля.
"PWM Library
CCP module is available with a number of PIC MCUs. mikroC PRO for PIC provides library which simplifies using PWM HW Module.

Note: Some MCUs have multiple CCP modules. In order to use the desired CCP library routine, simply change the number 1 in the prototype with the appropriate module number, i.e. PWM2_Start();

Library Routines
PWM1_Init
PWM1_Set_Duty
PWM1_Start
PWM1_Stop"
Более подробно смотреть во встроенном хелпе на микроС.

Всего таких PWM - 5 штук. Соответственно меняется цифра 1 на другую.
...
PWM1_Init
прототип - void PWM1_Init(const long freq);

Перевод Гугль: Инициализирует PWM модуль со скважностью 0. Параметр freq является искомой PWM частотой в Гц (см. лист данных устройства для правильного значения в связи с частотой кварца FOSC).
Эта процедура должна быть вызвана перед использованием других функций из библиотеки PWM.

Требуется использовать микроконтроллер с CCP модулем.

Расчет значения частоты ШИМ осуществляется компилятором. Таким образом, компилятор должен знать значение параметра во время компиляции, именно поэтому этот параметр необходимо объявить const константой, а не переменной.

Пример инициализации PWM модуля с частотой 5КГц:
Код: Выделить всёРазвернуть
PWM1_Init(5000);


Добавлено спустя 19 минут 59 секунд:
Re: Управление сервой с помощью потенциометра. МК PIC16F, язык С
PWM1_Set_Duty
Перевод Гугль:
Прототип - void PWM1_Set_Duty(unsigned short duty_ratio);

PWM скважность. Переменная duty_ratio принимает значения от 0 до 255, где 0 0%, 127 составляет 50%, и 255 = 100% скважностью. Другие конкретные значения скважности может быть рассчитана как (в процентах * 255) / 100.

PWM1_Init должна быть вызвана перед использованием этой процедуры.

Пример: Установить скважность 75%:
Код: Выделить всёРазвернуть
PWM1_Set_Duty (192);


Добавлено спустя 2 минуты 4 секунды:
Re: Управление сервой с помощью потенциометра. МК PIC16F, язык С
Старт и стоп описывать смысла нет и так понятно PWM1_Start и PWM1_Stop

Добавлено спустя 1 минуту 40 секунд:
Re: Управление сервой с помощью потенциометра. МК PIC16F, язык С
Перевод Гугль:
Пример библиотеки
Например, изменения PWM соотношение долг на RC1 и RC2 булавки постоянно. Если светодиод, связанных с этими контакта, можно наблюдать постепенное изменение излучаемого света.

Код: Выделить всёРазвернуть
unsigned short current_duty, old_duty, current_duty1, old_duty1;

void InitMain() {
  ANSEL  = 0;                         // Configure AN pins as digital I/O
  ANSELH = 0;
  PORTA = 255;
  TRISA = 255;                        // configure PORTA pins as input
  PORTB = 0;                          // set PORTB to 0
  TRISB = 0;                          // designate PORTB pins as output
  PORTC = 0;                          // set PORTC to 0
  TRISC = 0;                          // designate PORTC pins as output
  PWM1_Init(5000);                    // Initialize PWM1 module at 5KHz
  PWM2_Init(5000);                    // Initialize PWM2 module at 5KHz
}

void main() {
  InitMain();
  current_duty  = 16;                 // initial value for current_duty
  current_duty1 = 16;                 // initial value for current_duty1

  PWM1_Start();                       // start PWM1
  PWM2_Start();                       // start PWM2
  PWM1_Set_Duty(current_duty);        // Set current duty for PWM1
  PWM2_Set_Duty(current_duty1);       // Set current duty for PWM2

  while (1) {                         // endless loop
    if (RA0_bit) {                    // button on RA0 pressed
      Delay_ms(40);
      current_duty++;                 // increment current_duty
      PWM1_Set_Duty(current_duty);
     }

    if (RA1_bit) {                    // button on RA1 pressed
      Delay_ms(40);
      current_duty--;                 // decrement current_duty
      PWM1_Set_Duty(current_duty);
     }

    if (RA2_bit) {                    // button on RA2 pressed
      Delay_ms(40);
      current_duty1++;                // increment current_duty1
      PWM2_Set_Duty(current_duty1);
     }

    if (RA3_bit) {                    // button on RA3 pressed
      Delay_ms(40);
      current_duty1--;                // decrement current_duty1
      PWM2_Set_Duty(current_duty1);
     }

    Delay_ms(5);                      // slow down change pace a little
  }
}


P.S.: пример сам не проверял...

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

kotikov » 12 июл 2010, 15:06

Zeus, спасибо что перевел и все подробно расписал. Много стало понятнее.
boez, есть ли таймер или нет - не знаю, долго искал так и не нашел...

Только на счет PWM вряд ли. Библиотека используется с обычными двигателями. У меня на плате есть специальные разъемы для серв и ДПТ. Разъемы само собой разные. Всего 4 ДПТ объедены в PWM1 и PWM2. Каждый PWM объединяет в себе по 2 разъема и управляет двумя разъемами одновременно.

Мучился с кодом дальше. И чтобы работало результат пропорции пришлось разделить на 1000. Но работает с переменным успехом...
Серво соответствует движению потенциометра только в трех точках.
Крайнее правое, крайнее левое и среднее положение. Из-за того что пришлось разделить на 1000 значения для сервы равны 0, 1 или 2 (если точнее то они равны 0,500; 1,000; 2,400). 0 и 2 - это крайние положения. Но на 0 серву еще и колбасить начинает. Также не понятно почему ее колбасит во время движения. Почему серва чувствует только 3 позиции?

Впрочем предлагаю посмотреть видео:


Чтобы код был прозрачен для понимания добавлю подробные коменты. Прошу посмотреть:
Код: Выделить всёРазвернуть
char data1[6];
char data2[6];
int x;
unsigned i;
void main()
{
Lcd_Init(&PORTD);     
Lcd_Cmd(LCD_CURSOR_OFF); //курсор жки выключен
Lcd_Out(1,1,"ADC=");           
Lcd_Out(2,1,"servo=");     
TRISC.F5 =0;    //инициализация серв на трех портах
TRISB.F4 =0;
TRISB.F5 =0;


while(1)
      {
ANSEL=0xFF;          //PORTA назначаем анлоговым
TRISA=0xFF;          //PORTA назначаем входом
ADCON0=0b11001101;           //режим: RC_Mode (11) ANALOG1(0011) GO(0) ADON(1)
ADCON0.GO=1;          //запуск цикла аналогово-цифрового преобразователя
      while(ADCON0.GO);     // ожидание завершения преобразования
        {
       x=(ADRESH*4)+(ADRESL/64);  // расчет полученных данных
       WordToStr(x,data1);      //преобразуем Х для вывода на экран
       Lcd_Out(1,12,data1);  // выводим на экран
       Delay_ms(100);           // в течении 100 миллисекунд

      i=(1900/100)*(x/(1023/100));   // пропорция для зависимости частоты АЦП и импульсов для сервы
      i=i+500;                      // прибавляем 500 чтобы самое маленькое значение для сервы было не менее 500 (500 - это крайнее левое положение, правое 2400)
      i=i/1000;        // делим на 1000 для перевода микросекунд в миллисекунды, потому что функция Vdelay_ms(i); работает только с миллисекундами. Хотя этот момент я сам не очень понимаю.
                            // Если мы задаем i от 500-2400 то почему бы Vdelay_ms(i); не принимать их за миллисекунды? Впрочем если на 1000 не делить - то работать вообще не будет...

      WordToStr(i,data2);
      Lcd_Out(2,12,data2);   
      Delay_ms(100);         

ANSEL=0x00;
ANSELH=0x00;

PORTC.F5=1;          //команды работать соответствующим выходам
PORTB.F5=1;
PORTB.F4=1;
Vdelay_ms(i);   

PORTC.F5=0;
PORTB.F5=0;
PORTB.F4=0;
delay_ms(20);     // самое интересное, что если убрать эту строчку программа будет работать абсолютно также, и серва будет точно также колбаситься...
         }
       }
}
Последний раз редактировалось kotikov 12 июл 2010, 19:01, всего редактировалось 1 раз.

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

Zeus » 12 июл 2010, 17:58

Посмотрите:
1) http://www.rcdesign.ru/articles/radio/digital_servos
2) http://www.rcdesign.ru/articles/radio/servo_intro
там и про устройство и про "джиттер" написано - будет немного понятнее.

И чтобы отойти от микросекунд переведем все в милисекунды: на сервомашинку от приемника приходит импульсный сигнал с периодом 20 мс и с длительностью от 0,8 до 2,2 мс.

P.S.: код посмотрю, но чуть попозже времени не хватает... :(

Добавлено спустя 1 час 11 минут 40 секунд:
Re: Управление сервой с помощью потенциометра. МК PIC16F, язык С
Смотрю код и опять сразу вижу long i
Посмотри первую страницу - процедура Vdelay_ms(unsigned i);
компилятор микроСпро всетаки сыроват, может он сам и будет делать приведение типов, а может и нет, лучше указывать то, что требуют!
поэтому переправь в unsigned i;

И не совсем понял комментарий
x=(ADRESH*4)+(ADRESL/64); // вычисление частоты процессора

при чем здесь частота процессора?!

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

kotikov » 12 июл 2010, 19:47

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

long i - это жертва эксперемента. Правда до этого i была int. Компилятор и вправду сам делает приведение. Исправил.

в пару ADRESH и ADRESL записывается результат анлогово-цифрового преобразования и для того чтобы использовать полученные данные мы их вычисляем

Добавлено спустя 46 минут 55 секунд:
Zeus писал(а)::)).
2. Процедура VDelay_Advanced_ms (изначально предназначена для генерации милисекунд)
Prototype - void VDelay_Advanced_ms(unsigned time_in_ms, unsigned Current_Fosc_kHz);

Description Creates a software delay in duration of time_in_ms milliseconds (a variable), for a given oscillator frequency. Generated delay is not as precise as the delay created by Delay_ms.
Программная задержка в "реальных" "time_in_ms" милисекунах, которая привязана к частоте кварца.

Example pause = 1000;
fosc = 10000; (Частоту вводим в килогерцах, т.е. 10МГц/1000=10000 кГц)

VDelay_Advanced_ms(pause, fosc); // Generates approximately one second pause, for a oscillator frequency of 10 MHz

unsigned = 0...65535, так что максимум типа 65,535 МГц.
Так что четкие и правильные микросекунды этой процедурой не получить.
При кварце 10МГц можно конечно ввести 50000, тогда при time_in_ms = 1 может быть задержка будет 0,2 мс или 200 мкс...

Что в данной ситуации можно сделать и для чего можно применить эту функцию?
У меня к примеру кварц 20Мгц.

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

avr123.nm.ru » 12 июл 2010, 22:33

Правильно делать на таймере микросекунды.

А там нет функции паузы в такатах процессора как в IAR ?

Добавлено спустя 2 часа 2 минуты 55 секунд:
Zeus писал(а): При кварце 10МГц можно конечно ввести 50000, тогда при time_in_ms = 1 может быть задержка будет 0,2 мс или 200 мкс...


При кварце 10МГц нужно ввести 2000 чтобы укоротить паузы в 5 раз, или число 100 чтобы вместо мс реально получались 10-ки мкС - десятками уже можно серву грубо крутить.

Интересно бы в коде такой финт проверить и в PROTEUS.

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

Zeus » 12 июл 2010, 23:47

При кварце 10МГц нужно ввести 2000
- скорее всего так, типа занижаем тактовую частоту, а реально он на 10 МГц шпарит, может быть и так... я ошибочно увеличил в 5 раз.

Добавлено спустя 59 минут 30 секунд:
Re: Управление сервой с помощью потенциометра. МК PIC16F, язык С
в пару ADRESH и ADRESL записывается результат анлогово-цифрового преобразования и для того чтобы использовать полученные данные мы их вычисляем

Да это я понимаю, комментарий то написан "x=(ADRESH*4)+(ADRESL/64); // вычисление частоты процессора"
вот и спрашиваю причем здесь вычиление частоты процессора, когда вычисляешь данные с АЦП?

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

kotikov » 13 июл 2010, 00:03

Да это я понимаю, комментарий то написан "x=(ADRESH*4)+(ADRESL/64); // вычисление частоты процессора"
вот и спрашиваю причем здесь вычиление частоты процессора, когда вычисляешь данные с АЦП?

Очепятнулся Я.

Короче, я что-то запутался.
Как думаете правильный путь выбрал для решения данной задачи? Или вообще не в ту степь ушел? А то мне начинает казаться, что есть более простые решения, а я мучаюсь :crazy:

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

Zeus » 13 июл 2010, 00:23

я что-то не пойму, у тебя какой контроллер - PIC16F877?
вот сижу листаю даташит и не могу найти там никакого регистра
Код: Выделить всёРазвернуть
ANSEL=0xFF;          //PORTA назначаем анлоговым

и вот эти тоже:
Код: Выделить всёРазвернуть
ANSEL=0x00;
ANSELH=0x00;

в этих контроллерах вроде ADCON0 и ADCON1 только есть и ADRESH с ADRESL соответственно.
Регистр ADCON0 используется для настройки работы модуля АЦП, а с помощью регистра ADCON1 устанавливаются какие входы микроконтроллера будут использоваться модулем АЦП и в каком режиме (аналоговый или цмфровой порт вводаввывода).
Если у тебя действительно PIC16F877, то перечитай раздел 11 (модуль АЦП) даташита.
да ты не спеши все сделаешь и разберешься :)

Добавлено спустя 5 минут 55 секунд:
Re: Управление сервой с помощью потенциометра. МК PIC16F, язык С
можно узнать, а зачем вообще такая формула сложная: x=(ADRESH*4)+(ADRESL/64)
зачем ADRESH умножать на 4, а ADRESL делить на 64 и потом все это складывать?

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

avr123.nm.ru » 13 июл 2010, 01:07

Помоему код лучше из примеров компилятора брать.

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

kotikov » 13 июл 2010, 11:37

Zeus писал(а):я что-то не пойму, у тебя какой контроллер - PIC16F877?

Да! Именно 16F877 :)
Zeus писал(а):вот сижу листаю даташит и не могу найти там никакого регистра
Код: Выделить всёРазвернуть
ANSEL=0xFF;          //PORTA назначаем анлоговым

и вот эти тоже:
Код: Выделить всёРазвернуть
ANSEL=0x00;
ANSELH=0x00;

в этих контроллерах вроде ADCON0 и ADCON1 только есть и ADRESH с ADRESL соответственно.
Регистр ADCON0 используется для настройки работы модуля АЦП, а с помощью регистра ADCON1 устанавливаются какие входы микроконтроллера будут использоваться модулем АЦП и в каком режиме (аналоговый или цмфровой порт вводаввывода).
Если у тебя действительно PIC16F877, то перечитай раздел 11 (модуль АЦП) даташита.
да ты не спеши все сделаешь и разберешься :)


Я перечитал. Там и вправду ничего нет про ANSEL и ANSELH. По большей части я пользовался этой инструкцией, так как там плата идентична моей: http://www.terraelectronica.ru/pdf/INNOEXP/IE-ROBOPICA_RUS.pdf
Вот выдержки из инструкции (глава 5, страница 57):
ANSEL: регистр Выбора Аналогового Входа
Регистр ANSEL используется для конфигурирования входного режима линий ввода/вы- вода как аналоговых входов. Установка соответствующего бита ANSEL в состояние высо- кого уровня приводит к тому, что любое чтение цифрового состояния входа дает результат ‘0’, и позволяет для этого входа корректно выполняться аналоговым функциям.
1 = Аналоговый вход. Вывод назначен как аналоговый вход (по умолчанию).
0 = Цифровой Вход/Выход. Вывод назначен как порт, или для специальных функций.

ANSELH: Старший байт регистра Выбора Аналогового Входа
Регистр ANSELh используется для конфигурирования входного режима линий ввода/ вывода как аналоговых входов. Установка соответствующего бита ANSELh в состояние высокого уровня приводит к тому, что любое чтение цифрового состояния входа дает ре- зультат ‘0’, и позволяет для этого входа корректно выполняться аналоговым функциям. Регистр управляет конфигурированием выводов порта.
1 = Аналоговый вход. Вывод назначен как аналоговый вход (по умолчанию).
0 = Цифровой Вход/Выход. Вывод назначен как порт, или для специальных функций.

С твоей помощью думаю да :good:
Zeus писал(а):можно узнать, а зачем вообще такая формула сложная: x=(ADRESH*4)+(ADRESL/64)
зачем ADRESH умножать на 4, а ADRESL делить на 64 и потом все это складывать?

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

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

avr123.nm.ru » 13 июл 2010, 13:36

Лучше почитайте "Язык Си для микроконтроллеров" - http://proavr.narod.ru/05.htm - тогда будете понимать что написано в программе и осмысленно писать свою программу.

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

Zeus » 13 июл 2010, 18:27

Постараюсь помочь :) только как обычно времени очень мало свободного - сегодня только с Сызрани вернулся.
Ты ссылку дал на RoboPica - открываю эту ссылку и вижу микроконтроллер PIC16F887.
А у тебя PIC16F877 - с твоих слов.
Это разные контроллеры!
Или ты опечатался или у тебя действительно PIC16F877.

Добавлено спустя 5 минут 2 секунды:
Re: Управление сервой с помощью потенциометра. МК PIC16F, язык С
Дело в том, что PIC16F877 это устаревший контроллер
посмотри тут : http://www.microchip.com.ru/Midrange/mi ... tures.html

Помимо вышеприведенных микроконтроллеров, Microchip продолжает выпуск "устаревших" семейств (PIC16F627A/628A/648A, PIC16F72/73/74/76/77, PIC16F737/747/767/777, PIC16F818/819, PIC16F84(A), PIC16F87/88, PIC16F87x/87xA). Следует обратить внимание, что в большинстве случаев возможно и целесообразно осуществить переход к более новому семейству, обеспечивающему ту же функциональность, что позволит значительно сэкономить

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

kotikov » 13 июл 2010, 19:51

Очепятался. Вот этот - PIC16F887.

avr123.nm.ru, спасибо!

Re: Управление сервой с помощью потенциометра. МК PIC16F, яз

Zeus » 13 июл 2010, 23:23

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


Rambler\'s Top100 Mail.ru counter