AVR C代码中伺服电机与ADC的同步问题

yyhrrdl8  于 2023-11-16  发布在  其他
关注(0)|答案(2)|浏览(129)

我正在进行一个基于AVR ATMega32a微处理器的项目,我使用伺服电机从0度旋转到90度,同时捕获每个旋转度的ADC值。我的目标是使伺服电机的旋转与ADC值捕获同步,确保在进行下一个旋转度之前接收到每个旋转度的相应ADC值。
内容:
微控制器:我使用的是AVR微控制器ATmega32A。MG995伺服电机:伺服电机通过PWM控制,从0到90度旋转。ADC配置:我已将ADC配置为从光电二极管传感器读取值。连接:伺服电机和光电二极管传感器连接到微控制器。
我面临的问题是,在ADC值被传输到所有度数之前,伺服电机似乎旋转了90度。理想情况下,我希望电机以递增的方式旋转到每一度,并等待相应的ADC值,然后再进行下一个度数。
下面是我的C代码的相关部分的简化版本:

// Function to transmit a byte via UART
void UART_TxChar(char ch) {
    while (! (UCSRA & (1 << UDRE))); /* Wait for empty transmit buffer */
    UDR = ch;
}

// Function to send a string via UART
void UART_SendString(char *str) {
    unsigned char j = 0;
    while (str[j] != 0) /* Send string up to null */
    {
        UART_TxChar(str[j]);
        j++;
    }
}
...
// Function to rotate the servo to a specified degree
void RotateServo(uint16_t degree) {
    OCR1A = (degree * 125) / 9 + 175; // Calculate the OCR1A value for the specified degree
    _delay_ms(500); // Wait for the servo to reach the desired position
}

int main() {
    ...

    while (1) {
        for (int degree = 0; degree <= 90; degree++) {
            RotateServo(degree);

            // Read the ADC value from channel 0 (connected to photodiode)
            adc_value = adc_read(0);

            // Print the ADC value to the serial terminal
            sprintf(buffer, "Degree: %d, ADC Value: %d \n", degree, adc_value);
            UART_SendString(buffer);

            // Wait for a moment before moving to the next degree
            _delay_ms(1000);
        }

        _delay_ms(2000); // Wait for 2 seconds before reversing

        for (int degree = 90; degree >= 0; degree--) {
            RotateServo(degree);

            // Read the ADC value from channel 0 (connected to photodiode)
            adc_value = adc_read(0);

            // Print the ADC value to the serial terminal
            sprintf(buffer, "Degree: %d, ADC Value: %d \n", degree, adc_value);
            UART_SendString(buffer);

            // Wait for a moment before moving to the next degree
            _delay_ms(1000);
        }

        _delay_ms(1000); // Wait for 1 second before restarting the loop
    }

    return 0;
}

字符串
我尝试调整伺服电机运动和ADC值捕获之间的延迟,但这并没有解决问题。
我的期望是实现伺服电机的同步旋转和ADC值的捕获,逐度,确保电机不会移动到下一个Angular ,直到获得当前Angular 的ADC值。

  • 是否有一种方法可以同步伺服电机旋转与ADC值捕获的每度?
  • ADC值传输延迟是否有任何潜在原因?
  • 是否需要在代码或硬件设置中进行调整以实现同步?

如果您有任何见解、建议或解决方案可以帮助我解决此同步问题,我将不胜感激。谢谢您的帮助。
编辑:终于弄清楚了错误

// Function to rotate the servo to a specified degree
void RotateServo(uint16_t degree) {
    OCR1A = (degree * 125) / 90 + 175; // Calculate the OCR1A value for the specified degree
    _delay_ms(500); // Wait for the servo to reach the desired position
}


我犯的唯一错误是没有为指定的度数增加90,这使得我的电机在一圈内旋转10倍的速度。我的错
非常感谢所有以各种方式帮助我的人。

jxct1oxe

jxct1oxe1#

就目前而言,程序中的所有内容都依赖于其他内容。ADC传输和sprintf都非常耗时。而ADC周期与所有这些相比非常快,更不用说到处都有大量延迟。
所以第一步是把所有这些从“业余爱好者的地狱”转换成一个实际的实时系统,它可以同时做多件事。这意味着摆脱所有邪恶的_delay_ms,开始使用硬件外设定时器。你应该有一些处理ADC的东西,一些处理伺服器的东西和一些处理控制台的东西,这三件事不应该有紧密耦合的时间依赖性对彼此。
每个驱动程序都可以每隔1毫秒或更长时间触发一次回调,然后决定是否该采取行动。伺服器是否可用或忙碌-如果忙碌,那么显然我们不应该坐在那里无所事事地等待,而是留下定时器回调,让程序的其余部分在我们等待的时候做有意义的事情。伺服器和ADC也是如此。
根据ADC驱动程序的工作方式和ADC的设置,它可能返回上次读取的值,也可能实际上是忙等待轮询ADC以完成(这也会很糟糕)。有时你希望ADC需要手动踢来启动每次转换,在其他情况下,您希望使用“连续转换”功能,使ADC保持运行状态,并重复更新其数据寄存器,然后您只需获取最新值。

9lowa7mx

9lowa7mx2#

您的问题可能与ADC无关,而是用于生成PWM脉冲的定时器的代码,或者更准确地说-缺少它。
此外,它不清楚是否正确转换为0-90数字到一个正确的OCR1A值,对应于伺服的规格(典型的伺服有一个周期为20毫秒,其中第一个500- 2500 us是高脉冲)我看到没有#define为您的CPU频率,没有代码定义输出引脚的pwm输出,你基本上没有代码来控制正确移动电机的最重要部分。我建议深入到timer counter 1timer counter 1部分(第93页)。

相关问题