我有一台 Servo 电机(塔式微服务器99),我想通过模拟数字转换器控制 . 当adc读数为0伏时,电机应为0度;当adc读数为5伏时,电机应为180度 . 为了实现这一点,我使用了arduino uno(atmega328p微控制器) . 我将电压读数输入到电路板上的ADC中,读取该值,并根据该值生成一个具有适当占空比的周期来旋转电机 . 下面的代码用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和servo motor数据表 . 这些值似乎都是正确的 . 关于为什么句号可能出错的唯一可能的线索与我等待转换的while循环有关:
while((adc-> ADCSRA&0x10)== 0);
有趣的是,当我只是评论这个时,代码实际上完全有效,但我真的不明白为什么这样做会导致 - 我能想到的最好的是while循环在代码中产生足够的额外周期以稍微改变这段时间足以让0电压值产生意想不到的结果 . 然而,根据数据表,adc转换不应该花费很多周期(第一次转换大约需要15个周期,而第三次转换大约需要3个周期) - 但这不足以影响周期 .
这个问题的长短之处在于我相信我的ADC转换正在使我的代码给我一个不正确的时间段 . 为什么这种转换会给我这样的时期?还是我看错了方向?