- UID
- 1062083
- 性别
- 男
|
/*AVR控制20个舵机的程序*/
#include
#include
#include "me.h" //自定义的通用io简化位
void timer0_init(void);
void port_init(void);
void init(void);
void UART_init(void); //串口初始化程序
void UART_rx(void); //串口接收中断函数
void send_text(unsigned char *s); //字符串发送函数
void sendchar(unsigned char c); //字符发送函数
void dog_init(void); //初始化看门狗
unsigned char RX_data[4]={0}; //串口接收的数据
unsigned char RX_counter=0; //串口接收到的字节数计数器
unsigned char pwm1,pwm2,pwm3,pwm4,pwm5,pwm6,pwm7,pwm8,pwm9,pwm10,pwm11,pwm12,pwm13,pwm14,pwm15,pwm16,pwm17,pwm18,pwm19,pwm20; //分别为20个pwm的值
unsigned char count; //pwm定位变量
void main(void)
{
oSCCAL=0xAA; //系统时钟校准,不同的芯片和不同的频率
init(); //?
while(1)
{
WDR(); //拼命喂狗
if(RX_counter==4) //收到一个完整的命令信息
{
RX_counter=0; //清除串口接收到的字节数计数器
if((RX_data[0]==''''''''S'''''''')&&(RX_data[3]==''''''''E''''''''))//判断头尾是不是符合
{
CLI(); //关闭中断,开始判断数据
switch(RX_data[1])
{
case 0x01:
pwm1=RX_data[2];
break;
case 0x02:
pwm2=RX_data[2];
break;
case 0x03:
pwm3=RX_data[2];
break;
case 0x04:
pwm4=RX_data[2];
break;
case 0x05:
pwm5=RX_data[2];
break;
case 0x06:
pwm6=RX_data[2];
break;
case 0x07:
pwm7=RX_data[2];
break;
case 0x08:
pwm8=RX_data[2];
break;
case 0x09:
pwm9=RX_data[2];
break;
case 0x0a:
pwm10=RX_data[2];
break;
case 0x0b:
pwm11=RX_data[2];
break;
case 0x0c:
pwm12=RX_data[2];
break;
case 0x0d:
pwm13=RX_data[2];
break;
case 0x0e:
pwm14=RX_data[2];
break;
case 0x0f:
pwm15=RX_data[2];
break;
case 0x10:
pwm16=RX_data[2];
break;
case 0x11:
pwm17=RX_data[2];
break;
case 0x12:
pwm18=RX_data[2];
break;
case 0x13:
pwm19=RX_data[2];
break;
case 0x14:
pwm20=RX_data[2];
break;
default:
SEI(); //错误时打开中断,以便发送错误信息
send_text("ER"); //范围超出20个pwm,就发出大写字母"ER"
break;
}
SEI(); //恢复中断允许
send_text("OK"); //判断处理完毕返回ok;
}
}
}
}
void init(void)
{
CLI(); //disable all interrupts
port_init();
timer0_init();
TIMSK = 0x01; //定时器中断源
UART_init();
SEI(); //re-enable interrupts
}
void port_init(void)
{
PORTB = 0x00;
DDRB = 0xFF;
PORTC = 0x00;
DDRC = 0x7F;
PORTD = 0x00;
DDRD = 0xFF;
}
void send_char(unsigned char c) //发送单字符函数
{
while (!(UCSRA&(1 << UDRE))); //判断上次发送有没有完成
UDR = c; //发送数据
}
#pragma interrupt_handler UART_rx: iv_USART_RX //将串口接收中断,指给UART_rx
/********************************************************
通讯协议:S+PWM?+Volue+E
标志说明: S : 头标志 16进制:0x53
标志说明: PWM? : PWM标号,?范围:0x01~0x14 指20个pwm输出
标志说明: Volue: PWM占空比,范围0x00~0xff 关闭pwm则为0x00
标志说明: E : 结束标志 16进制:0x45
其他说明: 完整一个数据占4个byte,头尾必须分别为S、E方为有效
如果pwm位超出20个,返回字母ER;
********************************************************/
void UART_rx(void) //串口接收中断函数
{
RX_data[RX_counter] = UDR;
if (RX_data[RX_counter]==''''''''S'''''''') //纠正错位用,和RX_counter溢出。
{
RX_data[0]=RX_data[RX_counter];
RX_counter=0;
}
RX_counter++; //接收的字节数计数
}
void send_text(unsigned char *s) //字符串发送函数
{
while (*s)
{
send_char(*s);
s++;
}
}
void UART_init(void) //串口初始化程序
{
UCSRB = BIT(RXCIE)| BIT(RXEN) |BIT(TXEN); //允许串口发送和接收,并响应接收完成中断
UBRR = 51; //时钟8Mhz,波特率9600
UCSRC = BIT(URSEL)|BIT(UCSZ1)|BIT(UCSZ0); //8位数据+1位stop位
}
/********************************************
设计思路:舵机典型需要20mS的频率,即50Hz的频率
为了实现8位的pwm精度,需将20mS的时间再平均分正
256份,即78.125u秒,在一次中断的时候与目标定义
需要的pwm占空比的值(pwm1~20)比较,判断io是否该
输出0电平,如果不是则输出高电平
目标中断时间: 78.125uSec (加上0.2%误差)
实际中断时间: 78.000uSec
如果想提高频率,只需要修改定时器的溢出时间
如果想提高pwm的分辨率,则修改count的值
********************************************/
void timer0_init(void) //定时器初始化程序
{
TCCR0 = 0x00; //停止定时器
TCNT0 = 0xB4; //设置初始值
TCCR0 = 0x02; //开动定时器
}
#pragma interrupt_handler timer0_ovf_isr:10 //将定时器溢出中断指到timer0_ovf_isr中,好比汇编中的ORG
void timer0_ovf_isr(void) //定时器溢出中断程序
{
TCNT0 = 0xB4; //从新调入初始值
count++; //每中断一次加1
if (count<PWM1) 判断pwm1是不是改输出高电平 {
portc5_1;
}else{ //不是则输出0
portc5_0;
}
if (count<PWM2) </PWM2) {
portc4_1;
}else{
portc4_0;
}
if (count<PWM3) </PWM3) {
portc3_1;
}else{
portc3_0;
}
if (count<PWM4) </PWM4) {
portc2_1;
}else{
portc2_0;
}
if (count<PWM5) </PWM5) {
portc1_1;
}else{
portc1_0;
}
if (count<PWM6) </PWM6) {
portc0_1;
}else{
portc0_0;
}
if (count<PWM7) </PWM7) {
portb5_1;
}else{
portb5_0;
}
if (count<PWM8) </PWM8) {
portb4_1;
}else{
portb4_0;
}
if (count<PWM9) </PWM9) {
portb3_1;
}else{
portb3_0;
}
if (count<PWM10) </PWM10) {
portb2_1;
}else{
portb2_0;
}
if (count<PWM11) </PWM11) {
portb1_1;
}else{
portb1_0;
}
if (count<PWM12) </PWM12) {
portd2_1;
}else{
portd2_0;
}
if (count<PWM13) </PWM13) {
portd3_1;
}else{
portd3_0;
}
if (count<PWM14) </PWM14) {
portd4_1;
}else{
portd4_0;
}
if (count<PWM15) </PWM15) {
portb6_1;
}else{
portb6_0;
}
if (count<PWM16) </PWM16) {
portb7_1;
}else{
portb7_0;
}
if (count<PWM17) </PWM17) {
portd5_1;
}else{
portd5_0;
}
if (count<PWM18) </PWM18) {
portd6_1;
}else{
portd6_0;
}
if (count<PWM19) </PWM19) {
portd7_1;
}else{
portd7_0;
}
if (count<PWM20) </PWM20) {
portb0_1;
}else{
portb0_0;
}
}
void dog_init(void) //看门狗初始化
{
WDR(); //看门狗计数清零
WDTCR=0x0F; //使能看门狗,并且,采用2048分频,溢出时间5V时2.1S
}
//定时器T0中断,向8253发送控制字和数据
void T0Int() interrupt 1
{
TH0 = 0xB1;
TL0 = 0xE0; //20ms的时钟基准
//先写入控制字,再写入计数值
SERVO0 = 0x30; //选择计数器0,写入控制字
PWM0 = BUF0L; //先写低,后写高
PWM0 = BUF0H;
SERVO1 = 0x70; //选择计数器1,写入控制字
PWM1 = BUF1L;
PWM1 = BUF1H;
SERVO2 = 0xB0; //选择计数器2,写入控制字
PWM2 = BUF2L;
PWM2 = BUF2H;
}
关键字:AVR控制2 舵机 |
|