|
发表于 2017-2-18 13:10:37
|
显示全部楼层
我以前学习遥控器舵机的程序,有详细的注释,你可以参考一下。
main.c
--------------------------------
/****************************************************
RC 4VF模型遥控器PWM单片机解码与控制
-----------------------------------------------------
MCU :AVR M8
主 频 :8M
编制日期 :20110211
最后修订 :20110215
程序版本 :Ver1.1
-----------------------------------------------------
程序概述:
使用M8的TC1的外部触发分别测量RC PWM信号的各个脉冲
宽度,用测量得到的数据按通道分别输出相应控制舵机能够
是别的脉冲信号,实现对RC PWM的信号解调。
RC PWM参数请在TimeCount.h文件里配置。
程序借鉴了部分徐江的控制代码,特此声明。
-----------------------------------------------------
程序功能模块:
主程序模块
定时中断处理模块
串口调试模块
-----------------------------------------------------
RC PWM解码器硬件连接
AVR M8 MCU
-----------
PB0 #14 ICP输入 <-------- 4VF PWM 输出信号
PD0 #2 RXD输入 <-------- 串口数据输入
PD1 #3 TXD输出 --------> 串口数据输出
PD2 #4 --------> CH1舵机控制输出
PD3 #5 --------> CH2舵机控制输出
PD4 #6 --------> CH3舵机控制输出
PD5 #11 --------> CH4舵机控制输出
PD6 #12 --------> CH5舵机控制输出
PD7 #13 --------> CH6舵机控制输出
-----------------------------------------------------
程序修订记录:
2011.02.12
修改了中断捕获部分的代码,增加了几个标志变量,便于程
序进程控制。将PWM的配置数据和中断使用的全局变量移至
TimeCount.c文件中,在TimeCount.h中做了引用声明。
经过上机调试,发现捕获中断不能被关闭,否则会漏抓一帧
数据,导致输出不连续,分管输出信号的TC0可以在不使用
的时候关闭掉,减轻CPU的运算负担。
2011.02.15
将中断处理PWM输出改为函数处理PWM输出,在主程序中循环
调用PWM_OUT()函数即可实现连续PWM控制波形输出。
****************************************************/
#include "config.h"
void port_init(void)
{
PORTB = 0x00;
DDRB = 0x00;
PORTC = 0x00; //m103 output only
DDRC = 0x00;
PORTD = 0x00;
DDRD = 0x00;
}
//call this routine to initialize all peripherals
void init_devices(void)
{
//stop errant interrupts until set up
CLI(); //disable all interrupts
port_init();
Uart_Init();
TimeCount_init();
MCUCR = 0x00;
GICR = 0x00;
SEI(); //re-enable interrupts
//all peripherals are now initialized
}
void main (void)
{
init_devices();
while (1)
{
PWM_OUT();
}
}
TimeCount.c
---------------------------------------
/************************************************************************
文件名称:TimeCount.C
修改日期:2011/02/11
创建人:admvip
定时器0,定时器1的初始化和中断处理函数
************************************************************************/
#include <iom8v.h>
#include <AVRdef.h>
#include "TimeCount.h"
/*变量定义区*/
volatile unsigned char g_ChannelData[CHANNELCOUNT]; //通道数据存储数组
volatile unsigned char g_bInAllComplete = FALSE; //输入波形采集完成标志
volatile unsigned char g_bOutAllComplete = TRUE; //输出波形发送完成标志
volatile unsigned char g_nInIndex = 0; //当前输入通道号
volatile unsigned char g_nOutIndex = 0; //当前输出通道号
volatile unsigned int g_nPwmWidth = 0; //被检测脉冲宽度
volatile unsigned char g_bReceiveStart = FALSE; //通道数据接收开始标志
volatile unsigned int s_nTC0_Count;
/********************************************/
//TIMER0 initialize - prescale:64
// desired value: 8uSec
// actual value: 8.000uSec (0.0%)
static void timer0_init(void)
{
TCCR0 = 0x00; //stop
TCNT0 = 0xFF; //set count
//TCCR0 = 0x03; //start timer
}
//TIMER1 initialize - prescale:64
// WGM: 0) Normal, TOP=0xFFFF
// Setp value: 8.000uSec (0.0%)
static void timer1_init(void)
{
TCCR1B = 0x00; //stop
TCNT1H = 0x00; //setup
TCNT1L = 0x00;
OCR1AH = 0x00;
OCR1AL = 0x01;
OCR1BH = 0x00;
OCR1BL = 0x01;
ICR1H = 0x00;
ICR1L = 0x01;
TCCR1A = 0x00;
//TCCR1B bit6:1上升沿捕获 bit6:0下降沿捕获
//TCCR1B |= BIT(6);上升沿捕获 TCCR1B &= ~BIT(6);下降沿捕获
//根据需要可在捕获中断内变换
TCCR1B = 0x43; //start Timer
}
void TimeCount_init(void)
{
timer0_init();
timer1_init();
//TIMSK bit5:1使能TC1外部电平捕获 bit5:0禁止TC1外部电平捕获
TIMSK = 0x21; //timer interrupt sources
DDRD |= 0xFC; // 设置PD2-PD7为舵机信号输出端口
}
/***********************************************/
/***********************************************
TC0溢出中断负责各通道PWM波形输出,每8us刷新一
次,中断内检测各通道数据,到达脉宽设定的通道结
束PWM输出。全部通道发送完毕后所有端口置低电平,
同时关闭TC0溢出中断,等待下一次数据输出。
***********************************************/
#pragma interrupt_handler timer0_ovf_isr:iv_TIM0_OVF
void timer0_ovf_isr(void)
{
TCNT0 = 0xFF; //reload counter value
s_nTC0_Count++;
}
/***********************************************
TC1输入捕获中断负责接收RC送来的PWM连续脉冲,
通过检测各通道的脉冲宽度来确定各通道的PWM数据,
有效通道脉冲全部接收完毕后打开TC0溢出中断实现
各通道的PWM波形分离输出。
***********************************************/
#pragma interrupt_handler timer1_capt_isr:iv_TIM1_CAPT
void timer1_capt_isr(void)
{
//timer 1 input capture event, read (int)value in ICR1 using;
// value=ICR1L; //Read low byte first (important)
// value|=(int)ICR1H << 8; //Read high byte and shift into top byte
if (TCCR1B & BIT(6)) // 是否为上升沿触发?
{
TCNT1 = 0; // TC1计数器归零
TCCR1B &= ~BIT(6); // 修改为下降沿触发
} else
{
g_nPwmWidth = ICR1; // 下降沿触发后保存有效的脉宽数据
TCCR1B |= BIT(6); // 修改为上升沿触发,为下一次脉冲做准备
if (g_nPwmWidth > 625) // 判断是否为超过5ms的脉冲,确定通道数据是否开始接收
{
g_bReceiveStart = TRUE; // 接收开始标志置位
g_nInIndex = 0; // 输入索引归零
} else
{
if (g_bReceiveStart) // 判断接收开始标志置位,开始接受有效通道数据
{
g_ChannelData[g_nInIndex++] = (unsigned char) g_nPwmWidth;
if (g_nInIndex >= CHANNELCOUNT) // 检测有效通道接收是否完成
{
g_bReceiveStart = FALSE; // 接收开始标志清零
g_bInAllComplete = TRUE; // 接收完成标志置位
TCCR0 = 0x03; // 启动TC0,准备信号输出
}
}
}//if (g_nPwmWidth > 625) End
}//if (TCCR1B & BIT(6)) End
}
void PWM_OUT(void)
{
s_nTC0_Count = 0; //TC0计数器变量归零
while (g_bInAllComplete)
{
if (s_nTC0_Count < PULSEMaxWIDTH)
{
for (g_nOutIndex = 0; g_nOutIndex < CHANNELCOUNT; g_nOutIndex++)
{
if (g_ChannelData[g_nOutIndex] > s_nTC0_Count)
{
//按脉宽数据设置输出高电平,+2是为了调整端口索引和实际端口的差别,改变硬件连接时需要调整
PWMOUTPORT |= BIT(g_nOutIndex+2);
} else
{
//达到脉宽的通道电平置低
PWMOUTPORT &= ~BIT(g_nOutIndex+2);
}//if (g_ChannelData[g_nOutIndex] > s_nTC0_Count) End
}
} else
{
// 2.5ms时间到,所有舵机输出置低电平,结束一帧通道波形输出
PWMOUTPORT &= ~(BIT(CH1) | BIT(CH2) | BIT(CH3) | BIT(CH4) | BIT(CH5) | BIT(CH6));
if (s_nTC0_Count > 375)
{
//TCCR1B = 0x43; //start Timer
s_nTC0_Count = 0; //TC0计数器变量归零
g_bInAllComplete = FALSE;
TCCR0 = 0x00; //关闭TC0定时器,等待下一次输出
}
}//if (s_nTC0_Count < PULSEMaxWIDTH) End
}
}
|
|