现在的位置: 首页 > 综合 > 正文

利用51单片机按键设置ztw电调(修改3,验证OK,可以控制电机5级转速了)+mpu6050

2014年01月22日 ⁄ 综合 ⁄ 共 14526字 ⁄ 字号 评论关闭
/*
 ============================================================================
 Name        : ZTW_51.c
 Author      : clare_liu
 Version     :
 Copyright   : Your copyright notice
 Description : Hello World in C, Ansi-style
 ============================================================================
 */
//****************************************
// Update to MPU6050 by shinetop
// MCU: STC89C52
// 2012.3.1
// 功能: 显示加速度计和陀螺仪的10位原始数据
//****************************************
// GY-52 MPU3050 IIC测试程序
// 使用单片机STC89C51
// 晶振:11.0592M
// 显示:LCD1602
// 编译环境 Keil uVision2
// 参考宏晶网站24c04通信程序
// 时间:2011年9月1日
// QQ:531389319
//****************************************
#include <REG52.H>
#include <math.h>  //Keil library
#include <stdio.h>  //Keil library
#include <intrins.h>
typedef unsigned char uchar;
typedef unsigned short ushort;
typedef unsigned int uint;
//****************************************
// 定义51单片机端口
//****************************************
#define DataPort P0   //LCD1602数据端口
sbit SCL = P1 ^ 0; //IIC时钟引脚定义
sbit SDA = P1 ^ 1; //IIC数据引脚定义
sbit LCM_RS = P2 ^ 0; //LCD1602命令端口
sbit LCM_RW = P2 ^ 1; //LCD1602命令端口
sbit LCM_EN = P2 ^ 2; //LCD1602命令端口
sbit JDQ = P3 ^ 7; //继电器
//矩阵式键盘按键值的数码管显示实验
sbit P14 = P1 ^ 4; //将P14位定义为P1.4引脚
sbit P15 = P1 ^ 5; //将P15位定义为P1.5引脚
sbit P16 = P1 ^ 6; //将P16位定义为P1.6引脚
sbit P17 = P1 ^ 7; //将P17位定义为P1.7引脚
sbit P30 = P3 ^ 0; 
unsigned char code Tab[ ]= {0xc0,0xf9,0xa4,0xb0,0x99,0x92,0x82,0xf8,0x80,0x90}; //数字0~9的段码
unsigned char keyval; //定义变量储存按键值
//****************************************
// 定义MPU6050内部地址
//****************************************
#define SMPLRT_DIV  0x19 //陀螺仪采样率,典型值:0x07(125Hz)
#define CONFIG   0x1A //低通滤波频率,典型值:0x06(5Hz)
#define GYRO_CONFIG  0x1B //陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)
#define ACCEL_CONFIG 0x1C //加速计自检、测量范围及高通滤波频率,典型值:0x01(不自检,2G,5Hz)
#define ACCEL_XOUT_H 0x3B
#define ACCEL_XOUT_L 0x3C
#define ACCEL_YOUT_H 0x3D
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#define TEMP_OUT_H  0x41
#define TEMP_OUT_L  0x42
#define GYRO_XOUT_H  0x43
#define GYRO_XOUT_L  0x44
#define GYRO_YOUT_H  0x45
#define GYRO_YOUT_L  0x46
#define GYRO_ZOUT_H  0x47
#define GYRO_ZOUT_L  0x48
#define PWR_MGMT_1  0x6B //电源管理,典型值:0x00(正常启用)
#define WHO_AM_I  0x75 //IIC地址寄存器(默认数值0x68,只读)
#define SlaveAddress 0xD0 //IIC写入时的地址字节数据,+1为读取
//****************************************
//定义类型及变量
//****************************************
////////////////////////////////////////////////////////
/******************************************************************************
 *                    *
 *关于频率和占空比的确定,对于12M晶振,假定PWM输出频率为1KHZ,这样定时中断次数 *
 *设定为C=10,即0.01MS中断一次,则TH0=FF,TL0=F6;由于设定中断时间为0.01ms,这样*
 *可以设定占空比可从1-100变化。即0.01ms*100=1ms         *
 ******************************************************************************/
/*****************************************************************************
 * TH0和TL0是计数器0的高8位和低8位计数器,计算办法:TL0=(65536-C)%256;   *
 * TH0=(65536-C)/256,其中C为所要计数的次数即多长时间产生一次中断;TMOD是计数器*
 * 工作模式选择,0X01表示选用模式1,它有16位计数器,最大计数脉冲为65536,最长时 *
 * 间为1us*65536=65.536ms              *
 ******************************************************************************/
#define V_TH0  0Xff
#define V_TL0  0X38
#define V_TMOD 0X11
uchar dis[4]; //显示数字(-511至512)的字符数组
int dis_data; //变量
int Temperature, Temp_h, Temp_l; //温度及高低位数据
unsigned char ZKB1, ZKB2;

	uchar head=2;

	uchar slot_a=1;
	uchar ch_a=3;
	uchar ch_a_h=1;		//可调 1~2

	uchar slot_b=1;
	uchar ch_b=3;
	uchar ch_b_h=1;	   //可调  1~2

	uchar slot_c=1;
	uchar ch_c=3;
	uchar ch_c_h=1;	   //可调  1~2

	uchar slot_d=1;
	uchar ch_d=3;
	uchar ch_d_h=1;	  //可调  1~2
//****************************************
//函数声明
//****************************************
void delay(unsigned int k); //延时
//LCD相关函数
void InitLcd(); //初始化lcd1602
void lcd_printf(uchar *s, int temp_data);
void WriteDataLCM(uchar dataW); //LCD数据
void WriteCommandLCM(uchar CMD, uchar Attribc); //LCD指令
void DisplayOneChar(uchar X, uchar Y, uchar DData); //显示一个字符
void DisplayListChar(uchar X, uchar Y, uchar *DData, L); //显示字符串
//MPU6050操作函数
void InitMPU6050(); //初始化MPU6050
void Delay5us();
void I2C_Start();
void I2C_Stop();
void I2C_SendACK(bit ack);
bit I2C_RecvACK();
void I2C_SendByte(uchar dat);
uchar I2C_RecvByte();
void I2C_ReadPage();
void I2C_WritePage();
void display_ACCEL_x();
void display_ACCEL_y();
void display_ACCEL_z();
uchar Single_ReadI2C(uchar REG_Address); //读取I2C数据
void Single_WriteI2C(uchar REG_Address, uchar REG_data); //向I2C写入数据
//////////////////////////////////////////////////////////////////
void init_sys(void); /*系统初始化函数*/
void Delay5Ms(void);
//////////////////////////////////////////////////////////////////

/**************************************************************
 函数功能:数码管动态扫描延时
 **************************************************************/
void led_delay(void) {
	unsigned char j;
	for (j = 0; j < 200; j++)
		;
}
/**************************************************************
 函数功能:按键值的数码管显示子程序
 **************************************************************/
void display(unsigned char k,unsigned char z) {

	P2 = 0xf7; //点亮数码管DS0	  1111 0111
	P0 = Tab[z / 10]; //显示十位
	led_delay(); //动态扫描延时
	led_delay(); //动态扫描延时
	led_delay(); //动态扫描延时
	P0 = 0xff;

	P2 = 0xef; //点亮数码管DS1	   1110 1111 
	P0 = Tab[z % 10]; //显示个位
	led_delay(); //动态扫描延时
	led_delay(); //动态扫描延时
	led_delay(); //动态扫描延时
	P0 = 0xff;

	P2 = 0xbf; //点亮数码管DS6  1011 1111
	P0 = Tab[k / 10]; //显示十位
	led_delay(); //动态扫描延时
	led_delay(); //动态扫描延时
	led_delay(); //动态扫描延时
	P0 = 0xff;

	P2 = 0x7f; //点亮数码管DS7	 0111 1111
	P0 = Tab[k % 10]; //显示个位
	led_delay(); //动态扫描延时
	led_delay(); //动态扫描延时
	led_delay(); //动态扫描延时
	P0 = 0xff;

	
}
/**************************************************************
 函数功能:软件延时子程序
 **************************************************************/
void delay20ms(void) {
	unsigned char i, j;
	for (i = 0; i < 100; i++)
		for (j = 0; j < 60; j++)
			;
}
//****************************************
//整数转字符串
//****************************************
void lcd_printf(uchar *s, int temp_data) {
	if (temp_data < 0) {
		temp_data = -temp_data;
		*s = '-';
	} else
		*s = ' ';
	*++s = temp_data / 100 + 0x30;
	temp_data = temp_data % 100; //取余运算
	*++s = temp_data / 10 + 0x30;
	temp_data = temp_data % 10; //取余运算
	*++s = temp_data + 0x30;
}
//****************************************
//延时 k 毫秒
//****************************************
void delayNms(unsigned int k) {
	unsigned int i, j;
	for (i = 0; i < k; i++) {
		for (j = 0; j < 125; j++)
			;
	}
}
//****************************************
//LCD1602初始化
//****************************************
void InitLcd() {
	WriteCommandLCM(0x38, 1);
	WriteCommandLCM(0x08, 1);
	WriteCommandLCM(0x01, 1);
	WriteCommandLCM(0x06, 1);
	WriteCommandLCM(0x0c, 1);
	DisplayOneChar(0, 0, 'A');		
	DisplayOneChar(0, 1, 'G');	
}
//****************************************
//LCD1602写允许
//****************************************
void WaitForEnable(void) {
	DataPort = 0xff;
	LCM_RS = 0;
	LCM_RW = 1;
	_nop_();
	LCM_EN = 1;
	_nop_();
	_nop_();
	while (DataPort & 0x80)
		;
	LCM_EN = 0;
}
//****************************************
//LCD1602写入命令
//****************************************
void WriteCommandLCM(uchar CMD, uchar Attribc) {
	if (Attribc)
		WaitForEnable();
	LCM_RS = 0;
	LCM_RW = 0;
	_nop_();
	DataPort = CMD;
	_nop_();
	LCM_EN = 1;
	_nop_();
	_nop_();
	LCM_EN = 0;
}
//****************************************
//LCD1602写入数据
//****************************************
void WriteDataLCM(uchar dataW) {
	WaitForEnable();
	LCM_RS = 1;
	LCM_RW = 0;
	_nop_();
	DataPort = dataW;
	_nop_();
	LCM_EN = 1;
	_nop_();
	_nop_();
	LCM_EN = 0;
}
//****************************************
//LCD1602写入一个字符
//****************************************
void DisplayOneChar(uchar X, uchar Y, uchar DData) {
	Y &= 1;
	X &= 15;
	if (Y)
		X |= 0x40;
	X |= 0x80;
	WriteCommandLCM(X, 0);
	WriteDataLCM(DData);
}
//****************************************
//LCD1602显示字符串
//****************************************
void DisplayListChar(uchar X, uchar Y, uchar *DData, L) {
	uchar ListLength = 0;
	Y &= 0x1;
	X &= 0xF;
	while (L--) {
		DisplayOneChar(X, Y, DData[ListLength]);
		ListLength++;
		X++;
	}
}
//**************************************
//延时5微秒(STC90C52RC@12M)
//不同的工作环境,需要调整此函数
//当改用1T的MCU时,请调整此延时函数
//**************************************
void Delay5us() {
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
	_nop_();
}
//**************************************
//I2C起始信号
//**************************************
void I2C_Start() {
	SDA = 1; //拉高数据线
	SCL = 1; //拉高时钟线
	Delay5us(); //延时
	SDA = 0; //产生下降沿
	Delay5us(); //延时
	SCL = 0; //拉低时钟线
}
//**************************************
//I2C停止信号
//**************************************
void I2C_Stop() {
	SDA = 0; //拉低数据线
	SCL = 1; //拉高时钟线
	Delay5us(); //延时
	SDA = 1; //产生上升沿
	Delay5us(); //延时
}
//**************************************
//I2C发送应答信号
//入口参数:ack (0:ACK 1:NAK)
//**************************************
void I2C_SendACK(bit ack) {
	SDA = ack; //写应答信号
	SCL = 1; //拉高时钟线
	Delay5us(); //延时
	SCL = 0; //拉低时钟线
	Delay5us(); //延时
}
//**************************************
//I2C接收应答信号
//**************************************
bit I2C_RecvACK() {
	SCL = 1; //拉高时钟线
	Delay5us(); //延时
	CY = SDA; //读应答信号
	SCL = 0; //拉低时钟线
	Delay5us(); //延时
	return CY;
}
//**************************************
//向I2C总线发送一个字节数据
//**************************************
void I2C_SendByte(uchar dat) {
	uchar i;
	for (i = 0; i < 8; i++) //8位计数器
			{
		dat <<= 1; //移出数据的最高位
		SDA = CY; //送数据口
		SCL = 1; //拉高时钟线
		Delay5us(); //延时
		SCL = 0; //拉低时钟线
		Delay5us(); //延时
	}
	I2C_RecvACK();
}
//**************************************
//从I2C总线接收一个字节数据
//**************************************
uchar I2C_RecvByte() {
	uchar i;
	uchar dat = 0;
	SDA = 1; //使能内部上拉,准备读取数据,
	for (i = 0; i < 8; i++) //8位计数器
			{
		dat <<= 1;
		SCL = 1; //拉高时钟线
		Delay5us(); //延时
		dat |= SDA; //读数据
		SCL = 0; //拉低时钟线
		Delay5us(); //延时
	}
	return dat;
}
//**************************************
//向I2C设备写入一个字节数据
//**************************************
void Single_WriteI2C(uchar REG_Address, uchar REG_data) {
	I2C_Start(); //起始信号
	I2C_SendByte(SlaveAddress); //发送设备地址+写信号
	I2C_SendByte(REG_Address); //内部寄存器地址,
	I2C_SendByte(REG_data); //内部寄存器数据,
	I2C_Stop(); //发送停止信号
}
//**************************************
//从I2C设备读取一个字节数据
//**************************************
uchar Single_ReadI2C(uchar REG_Address) {
	uchar REG_data;
	I2C_Start(); //起始信号
	I2C_SendByte(SlaveAddress); //发送设备地址+写信号
	I2C_SendByte(REG_Address); //发送存储单元地址,从0开始
	I2C_Start(); //起始信号
	I2C_SendByte(SlaveAddress + 1); //发送设备地址+读信号
	REG_data = I2C_RecvByte(); //读出寄存器数据
	I2C_SendACK(1); //接收应答信号
	I2C_Stop(); //停止信号
	return REG_data;
}
//**************************************
//初始化MPU6050
//**************************************
void InitMPU6050() {
	Single_WriteI2C(PWR_MGMT_1, 0x00); //解除休眠状态
	Single_WriteI2C(SMPLRT_DIV, 0x07);
	Single_WriteI2C(CONFIG, 0x06);
	Single_WriteI2C(GYRO_CONFIG, 0x18);
	Single_WriteI2C(ACCEL_CONFIG, 0x01);
}
//**************************************
//合成数据
//**************************************
int GetData(uchar REG_Address) {
	char H, L;
	H = Single_ReadI2C(REG_Address);
	L = Single_ReadI2C(REG_Address + 1);
	return (H << 8) + L; //合成数据
}
//**************************************
//在1602上显示10位数据
//**************************************
void Display10BitData(int value, uchar x, uchar y) {
	value /= 64; //转换为10位数据
	lcd_printf(dis, value); //转换数据显示
	DisplayListChar(x, y, dis, 4); //启始列,行,显示数组,显示长度
}
//**************************************
//显示温度
//**************************************
void display_temp() {
	Temp_h = Single_ReadI2C(TEMP_OUT_H); //读取温度
	Temp_l = Single_ReadI2C(TEMP_OUT_L); //读取温度
	Temperature = Temp_h << 8 | Temp_l; //合成温度
	Temperature = 35 + ((double) (Temperature + 13200)) / 280; // 计算出温度
	lcd_printf(dis, Temperature); //转换数据显示
	DisplayListChar(11, 1, dis, 4); //启始列,行,显示数组,显示位数
}
//延时
void Delay5Ms(void) {
	unsigned int TempCyc = 100000;
	while (TempCyc--)
		;
}
/******************************************************
 *函数功能:对系统进行初始化,包括定时器初始化和变量初始化*/
void init_timer_interrupt(void) /*系统初始化函数*/
{
	/*PWM定时器T0初始化,每隔0.2ms产生一次中断,共计100次,负责产生周期为20ms的PPM信号*/
	TMOD = V_TMOD;
	TH0 = V_TH0;
	TL0 = V_TL0;
	/*keyboard 定时器T1初始化,*/
	TH1 = (65536 - 500) / 256; //定时器T0的高8位赋初值
	TL1 = (65536 - 500) % 256; //定时器T0的高8位赋初值
	keyval = 0x00; //按键值初始化为0
	/*打开并使能中断,启动定时器T0和T1*/
//	EA = 1; //开总中断
//	ET0 = 1;  //定时器T0中断允许 	
//	TR0 = 1;  //启动定时器T0
//	ET1 = 1; //定时器T1中断允许
//	TR1 = 1; //启动定时器T1
//  EX0=1;  //允许使用外中断
//  IT0=1;  //选择负跳变来触发外中断
//	EX1=1;  //允许使用外中断
//  IT1=1;  //选择负跳变来触发外中断
}
//*********************************************************
//主程序
//*********************************************************
void main() {

	/**************************/
	/*****T0 & T1 initial******/
	/**************************/
	init_timer_interrupt();
	/**************************/
	/*****mpu6050 initial******/
	/**************************/
 	delayNms(500);  //上电延时500ms
	InitLcd();  	//液晶初始化
	InitMPU6050(); //初始化MPU6050
	delayNms(150);	   //上电延时150ms
	 while(1)
 	{
  		Display10BitData(GetData(ACCEL_XOUT_H),2,0); //显示X轴加速度
  		Display10BitData(GetData(ACCEL_YOUT_H),7,0); //显示Y轴加速度
  		Display10BitData(GetData(ACCEL_ZOUT_H),12,0); //显示Z轴加速度
 		Display10BitData(GetData(GYRO_XOUT_H),2,1); //显示X轴角速度
 		Display10BitData(GetData(GYRO_YOUT_H),7,1); //显示Y轴角速度
 		Display10BitData(GetData(GYRO_ZOUT_H),12,1); //显示Z轴角速度
  		delayNms(500);
	 }
	
	ZKB1 = 5; /*占空比初始值设定*/
	ZKB2 = 99; /*占空比初始值设定*/
	while (1) //无限循环
	{
		//keyboard event
		display(keyval,ZKB1); //调用按键值的数码管显示子程序
		led_delay(); //动态扫描延时		
		//hand event
		if (!P1_4) //如果按了+键,增加占空比
		{
			Delay5Ms();
			if (!P1_4) {
				ZKB1++;
				JDQ = ~JDQ;
			}
		}
		if (!P1_5) //如果按了-键,减少占空比
		{
			Delay5Ms();
			if (!P1_5) {
				ZKB1--;
				JDQ = ~JDQ;
			}
		}
		/*对占空比值限定范围*/
		if (ZKB1 > 10)
			ZKB1 = 5;
		if (ZKB1 < 5)
			ZKB1 = 10;
	}
}
/**************************************************************
 函数功能:外部中断1
 **************************************************************/
void EXT_INIT0_interserve(void) interrupt 0 using 0
{
		ZKB1++;
		ZKB2 = 100 - ZKB1;
		JDQ = ~JDQ;
}
/**************************************************************
 函数功能:定时器T0的中断服务子程序,每隔0.2ms产生一次中断,重复100次,构成周期为20ms的周期信号(PPM信号)
 **************************************************************/
void timer0_interserve(void) interrupt 1 using 1
{
	static uchar click=0; /*中断次数计数器变量*/
	TH0=V_TH0; /*恢复定时器初始值*/
	TL0=V_TL0;
	TR0 = 1;  //启动定时器T0
	++click;
	if (click>100) click=0;	
	/****
	 * 根据keyval值设置占空比ZKB1的大小(1~99),输出所需要的PPM信号
	 * ***/	
	if (click<=ZKB1) /*当小于占空比值时输出低电平,高于时是高电平,从而实现占空比的调整*/
	{	P1_0=1;	}
	else
	{	P1_0=0; }
}
/**************************************************************
 函数功能:外部中断1
 **************************************************************/
void EXT_INIT1_interserve(void) interrupt 2 using 2
{
	ZKB1--;
	ZKB2 = 100 - ZKB1;
	JDQ = ~JDQ;
}
/**************************************************************
 函数功能:定时器T1的中断服务子程序,进行键盘扫描,判断键位,产生中断间隔为500us=0.5ms
 **************************************************************/
void timer1_interserve(void) interrupt 3 using 3 //定时器T1的中断编号为3,使用第3组寄存器
{
	TR1=0; //关闭定时器T0
	P1=0xf0;//所有行线置为低电平“0”,所有列线置为高电平“1”
	if((P1&0xf0)!=0xf0)//列线中有一位为低电平“0”,说明有键按下
	delay20ms();//延时一段时间、软件消抖
	if((P1&0xf0)!=0xf0)//确实有键按下
	{
		P1=0xfe; //第一行置为低电平“0”(P1.0输出低电平“0”)
		if(P14==0)//如果检测到接P1.4引脚的列线为低电平“0”
		keyval=1;//可判断是S1键被按下
		if(P15==0)//如果检测到接P1.5引脚的列线为低电平“0”
		keyval=2;//可判断是S2键被按下
		if(P16==0)//如果检测到接P1.6引脚的列线为低电平“0”
		keyval=3;//可判断是S3键被按下
		if(P17==0)//如果检测到接P1.7引脚的列线为低电平“0”
		keyval=4;//可判断是S4键被按下

		P1=0xfd;//第二行置为低电平“0”(P1.1输出低电平“0”)
		if(P14==0)//如果检测到接P1.4引脚的列线为低电平“0”
		keyval=5;//可判断是S5键被按下
		if(P15==0)//如果检测到接P1.5引脚的列线为低电平“0”
		keyval=6;//可判断是S6键被按下
		if(P16==0)//如果检测到接P1.6引脚的列线为低电平“0”
		keyval=7;//可判断是S7键被按下
		if(P17==0)//如果检测到接P1.7引脚的列线为低电平“0”
		keyval=8;//可判断是S8键被按下

		P1=0xfb;//第三行置为低电平“0”(P1.2输出低电平“0”)
		if(P14==0)//如果检测到接P1.4引脚的列线为低电平“0”
		keyval=9;//可判断是S9键被按下
		if(P15==0)//如果检测到接P1.5引脚的列线为低电平“0”
		keyval=10;//可判断是S10键被按下
		if(P16==0)//如果检测到接P1.6引脚的列线为低电平“0”
		keyval=11;//可判断是S11键被按下
		if(P17==0)//如果检测到接P1.7引脚的列线为低电平“0”
		keyval=12;//可判断是S12键被按下

		P1=0xf7;//第四行置为低电平“0”(P1.3输出低电平“0”)
		if(P14==0)//如果检测到接P1.4引脚的列线为低电平“0”
		keyval=13;//可判断是S13键被按下
		if(P15==0)//如果检测到接P1.5引脚的列线为低电平“0”
		keyval=14;//可判断是S14键被按下
		if(P16==0)//如果检测到接P1.6引脚的列线为低电平“0”
		keyval=15;//可判断是S15键被按下
		if(P17==0)//如果检测到接P1.7引脚的列线为低电平“0”
		keyval=16;//可判断是S16键被按下
	}
	TR1=1; //开启定时器T0
	TH1=(65536-500)/256;//定时器T0的高8位赋初值
	TL1=(65536-500)%256;//定时器T0的高8位赋初值
}

 

抱歉!评论已关闭.