You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Atmega32a通过C程序控制4路舵机PWM信号的问题排查

ATmega32A舵机控制与UART通信问题

需求与问题

  • 目标:使用ATmega32A单片机,配置PORTD的Pin3、Pin4、Pin5、Pin7引脚输出PWM信号,控制4个舵机各自旋转90度;通过UART接收C++程序发送的数据,指定舵机动作。
  • 遇到的问题:
    1. 初始代码无法实现预期舵机控制功能
    2. 改进后代码可控制舵机旋转,但无法通过发送新PWM信号改变同一引脚对应舵机的位置
    3. UART通信无法与主程序并行运行

初始代码

/*
 * send my name.c
 *
 * 创建时间: 04/04/2024 10.32.40
 * 作者 : chris
 */ 
#define F_CPU 8000000UL             // 定义CPU频率
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sfr_defs.h>
#include <stdlib.h>

void setup_uart() {
    // 设置波特率
    UBRRH =  0;                             // 计算得F_CPU/16(波特率)-1 = 51,数值小于255,仅需设置低字节
    UBRRL =  51;                            
    UCSRB = (1 << TXEN)|(1 << RXEN);        // 启用发送和接收
}

void uart_transmit(uint8_t data) {
    // 等待发送缓冲区为空
    while (!(UCSRA & (1 << UDRE)));

    // 将数据放入缓冲区,发送数据
    UDR = data;
}

uint8_t uart_receive() {
    // 等待数据接收完成
    while (!(UCSRA & (1 << RXC)));

    // 获取并返回缓冲区中的接收数据
    return UDR;
}

///////////////////// 配置舵机与PWM信号

void PWM_gen(){
    DDRD = 0xFF; // 设置D口为输出
    
    TCCR1A |= 1<<WGM11 | 1<<COM1A1 | 1<<COM1B1; // 配置定时器1为快速PWM模式,OC1A非反相输出
    TCCR1B |= 1<<WGM12 | 1<<WGM13  | 1<<CS10; // 设置定时器1预分频器为1(无分频),快速PWM模式
    TIMSK  |= 1<<OCIE1A; // 启用OC1A输出比较中断
    
    ICR1 = 19999; // 计数器最大值,用于计算占空比

    sei(); // 启用全局中断,触发中断服务程序(ISR)将PORTD置为0xFF(所有引脚置高)
    
    DDRD |= (1 << PORTD3) | (1 << PORTD4) | (1 << PORTD5) | (1 << PORTD7); // 设置PD3、PD4、PD5、PD7为输出引脚
}

void servo_rotate(uint8_t servo_number){
    switch(servo_number) {
        case 1:
            if (TCNT1 >= 800 && TCNT1 <= 2300)
            {
                if (TCNT1 >= 1000 && bit_is_set(PORTD , PIND3)) // 当TCNT1大于等于1000且PD3为高电平时,拉低PD3
                {
                    PORTD &= ~(1<<PIND3);
                }
            }
        break;
        
        case 2:
            if (TCNT1 >= 800 && TCNT1 <= 2300)
            {
                if (TCNT1 >= 1000 && bit_is_set(PORTD , PIND4)) // 当TCNT1大于等于1000且PD4为高电平时,拉低PD4
                {
                    PORTD &= ~(1<<PIND4);
                }
            }
        break;
        
        case 3:
            if (TCNT1 >= 800 && TCNT1 <= 2300)
            {
                if (TCNT1 >= 1000 && bit_is_set(PORTD , PIND5)) // 当TCNT1大于等于1000且PD5为高电平时,拉低PD5
                {
                    PORTD &= ~(1<<PIND5);
                }
            }
        break;
        
        case 4:
            if (TCNT1 >= 800 && TCNT1 <= 2300)
            {
                if (TCNT1 >= 1000 && bit_is_set(PORTD , PIND7)) // 当TCNT1大于等于1000且PD7为高电平时,拉低PD7
                {
                    PORTD &= ~(1<<PIND7);
                }
            }
        break;
        
        default:
        break;
    }
}

ISR(TIMER1_COMPA_vect){
    PORTD = 0xFF;
}

int main(void) {
    PWM_gen();
    setup_uart();

    while (1) {
        servo_rotate(1);
        _delay_ms(1000);
        servo_rotate(2);
        _delay_ms(1000);
        servo_rotate(3);
        _delay_ms(1000);
        servo_rotate(4);

        _delay_ms(1000);
    }
}

改进后代码

// 可运行代码(每个引脚固定运动)

#define F_CPU 1000000UL             // 定义CPU频率

#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sfr_defs.h>

// UART配置
void setup_uart() {
    // 设置波特率
    UBRRH =  0;                             // 计算得F_CPU/16(波特率)-1 = 12,数值小于255,仅需设置低字节
    UBRRL =  12;                            
    UCSRB = (1 << TXEN)|(1 << RXEN);        // 启用发送和接收
}

void uart_transmit(uint8_t data) {
    // 等待发送缓冲区为空
    while (!(UCSRA & (1 << UDRE)));

    // 将数据放入缓冲区,发送数据
    UDR = data;
}

uint8_t uart_receive() {
    // 等待数据接收完成
    while (!(UCSRA & (1 << RXC)));

    // 获取并返回缓冲区中的接收数据
    return UDR;
}

// PWM与舵机运动配置
void set_timer_pwm(){
    DDRD = 0xFF; // 设置D口为输出
    
    TCCR1A |= 1<<WGM11;   // 配置定时器1为快速PWM模式
    TCCR1B |= 1<<WGM12 | 1<<WGM13  | 1<<CS10; // 设置定时器1预分频器为1(无分频),快速PWM模式
    TIMSK  |= 1<<OCIE1A; // 启用OC1A输出比较中断
    
    ICR1 = 19999; // 计数器最大值,用于计算占空比

    sei(); // 启用全局中断,触发中断服务程序(ISR)将PORTD置为0xFF(所有引脚置高)
    
    DDRD |= (1 << PORTD5) | (1 << PORTD4) | (1 << PORTD3) | (1 << PORTD7); // 设置PD3、PD4、PD5、PD7为输出引脚
}

void rot_servo(uint8_t servo_nummer){
    switch (servo_nummer){
        case 1: 
            if (TCNT1 >= 1000 && bit_is_set(PORTD , PIND3)) // 当TCNT1大于等于1000且PD3为高电平时,拉低PD3
            {
                PORTD &= ~(1<<PIND3);
            }
        break; 
        
        case 2:
            if (TCNT1 >= 1000 && bit_is_set(PORTD , PIND4))
            {
                PORTD &= ~(1<<PIND4);
            }
        break;
        
        case 3:
            if (TCNT1 >= 1000 && bit_is_set(PORTD , PIND5))
            {
                PORTD &= ~(1<<PIND5);
            }
        break;
        
        case 4:
        if (TCNT1 >= 1000 && bit_is_set(PORTD , PIND7))
        {
            PORTD &= ~(1<<PIND7);
        }
        break;
    }
}

void rot_servo_back(uint8_t servo_nummer){
    switch (servo_nummer){
        case 1:
        if (TCNT1 >= 2000 && bit_is_set(PORTD , PIND3)) // 当TCNT1大于等于2000且PD3为高电平时,拉低PD3
        {
            PORTD &= ~(1<<PIND3);
        }
        break;
        
        case 2:
        if (TCNT1 >= 2000 && bit_is_set(PORTD , PIND4))
        {
            PORTD &= ~(1<<PIND4);
        }
        break;
        
        case 3:
        if (TCNT1 >= 2000 && bit_is_set(PORTD , PIND5))
        {
            PORTD &= ~(1<<PIND5);
        }
        break;
        
        case 4:
        if (TCNT1 >= 2000 && bit_is_set(PORTD , PIND7))
        {
            PORTD &= ~(1<<PIND7);
        }
        break;
    }
}

int main(void) {
    set_timer_pwm(); 
    setup_uart(); 

    while(1){
        if (TCNT1 >= 800 && TCNT1 <= 2300)
        {
            rot_servo(1);
            rot_servo_back(2); 
            rot_servo(3);
            rot_servo_back(4); 
            // 以下代码无法生效
            rot_servo(1);
            rot_servo_back(1);
        }
    }
    return 0;
}

ISR(TIMER1_COMPA_vect){
    PORTD = 0xFF;
}

内容的提问来源于stack exchange,提问作者Chris230199

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 03:05:55