Сервомотор, управляемый с помощью АЦП: неправильный период - PullRequest
0 голосов
/ 09 декабря 2018

У меня есть серводвигатель (Tower Pro Micro Server 99), которым я хочу управлять через аналоговый цифровой преобразователь.Когда АЦП показывает 0 Вольт, двигатель должен быть в 0 градусов;когда АЦП показывает 5 вольт, двигатель должен быть на 180 градусов.Для этого я использовал arduino uno (микроконтроллер atmega328p).Я подал свое показание напряжения в АЦП на плате, прочитал значение и на основе этого значения я сгенерировал период с соответствующим рабочим циклом для вращения двигателя.Код ниже делает это, написанный на C:

#include <stdlib.h>
#include "eel4746c.h"

void delay_8us()
{
  tc0->TCCRA=0x00; //set mode (normal) and prescaler (64)
  tc0->TCCRB=0x03; 
  tc0->TCNT=255;   //set counter value to 255
  *TIFR0=1;        //clear TOVO flag in TIFR0 register
  while((*TIFR0&0x01)==0); //count until overflow, results in 8us delay
}

void delay_in_8us(uint16_t x) //calls 8us delay a certain amount of times
{
  uint16_t i;
  for (i=0;i<x;i++) delay_8us();
}

int main()
{

uint16_t T=2500;   //define max period value, 8us * 2500 is 20ms
uint16_t value=75; //values that defines duty cycle of period
portd->DDR=0x01;   //sets pin 0 and port D as output

adc->ADCSRA=0x87;  //enables ADC, sets scale to 128
adc->ADMUX=0x40;   //sets AREF pin, sets ADC0 pin as input for ADC
uint16_t adcL=0;   //variables to read in values
uint16_t adcH=0;
uint32_t adcR=0;

while(1){

  adc->ADCSRA= adc->ADCSRA | 0x40;  //starts ADC conversion
  while ((adc->ADCSRA&0x10) == 0 ); //waits for ADC conversion to finish
  adcL = adc->ADCL;                 //read in ADC results
  adcH = adc->ADCH & 0x0003;
  adcR = (256*adcH)+adcL;           //convert ADC results to integer value
  value = 75 + ((180)*(adcR) / 1023); //calculates value from ADC value to 
                                      //set period


  portd->PORT=0x01; //sets port d, pin 0 to 1
  delay_in_8us(value); //delays 8us * value

  portd->PORT=0x00; //sets port d, pin 0 to 0
  delay_in_8us(T - value); //delays 8us * (2500 - value) to get rest of 
                           //period
}

}

Заголовочный файл (только содержит определения портов и расположения / структуры памяти):

#include <stdint.h>

#ifndef _EEL4746C_H_
#define _EEL4746C_H_


// Structure for GPIO ports
typedef struct gpio_port_struct {
    uint8_t PIN;          // Input Register
    uint8_t DDR;          // Data Directional Register
    uint8_t PORT;         // Output Register
} gpio_port_t;

// The three GPIO ports (B,C, and D)
volatile gpio_port_t *portb=(gpio_port_t *) 0x0023;
volatile gpio_port_t *portc=(gpio_port_t *) 0x0026;
volatile gpio_port_t *portd=(gpio_port_t *) 0x0029;


// 8-bit Time/Counter Structure
typedef struct timer_counter_8bit_struct {
    uint8_t TCCRA;        // Control Register A
    uint8_t TCCRB;        // Control Register B
    uint8_t TCNT;         // Counter (value)
    uint8_t OCRA;         // Output Compare Register A
    uint8_t OCRB;         // Output Compare Register B
} timer_counter_8bit_t;

// Timer/Counter 0
volatile timer_counter_8bit_t *tc0=(timer_counter_8bit_t *) 0x0044;

// Timer/Counter 0 Interrupt Flag Register
volatile uint8_t *TIFR0= (uint8_t *) 0x0035;

// Timer/Counter 0 Interrupt Mask Register
volatile uint8_t *TIMSK0=(uint8_t *) 0x006e;

// Analog to Digital Converter Structure
typedef struct adc_struct {
  uint8_t ADCL;           // ADC Data Register Low
  uint8_t ADCH;           // ADC Data Register High
  uint8_t ADCSRA;         // ADC Control and Status Register A
  uint8_t ADCSRB;         // ADC Control and Status Register B
  uint8_t ADMUX;          // ADC Multiplexer Selection Register
  uint8_t not_used;
  uint8_t DIDR0;          // Digital Input Disable Register 0
  uint8_t DIDR1;          // Digital Input Disable Register 1
} adc_t;

// ADC
volatile adc_t *adc=(adc_t *) 0x0078;

#endif

Когда я пишу этот код в arduinoи запустить его, это в основном работает хорошо.Я просто считал напряжение на выводе ADC0 с помощью потенциометра.Единственная проблема, которую я имею, состоит в том, что при низких напряжениях (около 0) двигатель постоянно вращается.Это говорит мне о том, что период неверен, однако, глядя на мой код, я не вижу способа получить неправильный период.

До сих пор я дважды проверял, все использует / ожидает правильногозначение;проверено на соответствие ATmega328p и сервомотору спецификациям.Все эти значения кажутся правильными.Единственный возможный способ понять, почему период может быть неправильным, связан с циклом while, где я жду преобразования:

while ((adc-> ADCSRA & 0x10) == 0);

Интересно, что код фактически работает полностью, когда я просто комментирую это, но я не совсем понимаю, почему это приводит к такому результату - лучшее, что я могу понять, это то, что цикл while генерирует достаточноЦиклы в коде слегка изменяют период, достаточный для того, чтобы нулевое значение напряжения привело к чему-то непредвиденному.Однако, согласно данным, преобразование АЦП не должно занимать много циклов (что-то около 15 циклов для первого преобразования и 3 после этого) - однако этого не должно быть достаточно, чтобы повлиять на период.

Суть этого вопроса в том, что я считаю, что из-за моего преобразования АЦП мой код дал мне неправильный период.Почему это обращение дало бы мне такой период?Или я смотрю не в ту сторону?

...