Atmega32a通过C程序控制4路舵机PWM信号的问题排查
ATmega32A舵机控制与UART通信问题
需求与问题
- 目标:使用ATmega32A单片机,配置PORTD的Pin3、Pin4、Pin5、Pin7引脚输出PWM信号,控制4个舵机各自旋转90度;通过UART接收C++程序发送的数据,指定舵机动作。
- 遇到的问题:
- 初始代码无法实现预期舵机控制功能
- 改进后代码可控制舵机旋转,但无法通过发送新PWM信号改变同一引脚对应舵机的位置
- 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
相关产品推荐
相关产品推荐

