#include <iom8v.h>
#include <macros.h>
#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)
{
portc4_1;
}else{
portc4_0;
}
if (count<pwm3)
{
portc3_1;
}else{
portc3_0;
}
if (count<pwm4)
{
portc2_1;
}else{
portc2_0;
}
if (count<pwm5)
{
portc1_1;
}else{
portc1_0;
}
if (count<pwm6)
{
portc0_1;
}else{
portc0_0;
}
if (count<pwm7)
{
portb5_1;
}else{
portb5_0;
}
if (count<pwm8)
{
portb4_1;
}else{
portb4_0;
}
if (count<pwm9)
{
portb3_1;
}else{
portb3_0;
}
if (count<pwm10)
{
portb2_1;
}else{
portb2_0;
}
if (count<pwm11)
{
portb1_1;
}else{
portb1_0;
}
if (count<pwm12)
{
portd2_1;
}else{
portd2_0;
}
if (count<pwm13)
{
portd3_1;
}else{
portd3_0;
}
if (count<pwm14)
{
portd4_1;
}else{
portd4_0;
}
if (count<pwm15)
{
portb6_1;
}else{
portb6_0;
}
if (count<pwm16)
{
portb7_1;
}else{
portb7_0;
}
if (count<pwm17)
{
portd5_1;
}else{
portd5_0;
}
if (count<pwm18)
{
portd6_1;
}else{
portd6_0;
}
if (count<pwm19)
{
portd7_1;
}else{
portd7_0;
}
if (count<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;
}
[font=宋体]文字[/font]