2013年全国大学生电子设计竞赛
简易旋转倒立摆及控制装置(C题)
【本科组】
2013年9月7
日
摘要
本题要求设计一个简易旋转倒立摆及控制系统,其中角度传感器、步进电机和单片机
890C521是系统核心部件。系统接收角度传感器反馈的信号,通过PCF8591将接收的信号转换成数字信号,将数值送入单片机中进行计算,可得出摆杆的位置,进而单片机控制步进电机,对摆杆进行控制,达到所要的旋转或者倒立的控制目标。
关键词:简易旋转倒立摆
步进电机
单片机 角度传感器
目录
1 设计任务及要求 .................................................... 5
1.1 设计任务 .................................................... 5 1.2 基本要求 ................................................... 5 2主控制器件的论证与选择 ............................................ 6
2.1控制器选用 ................................................... 6 2.2控制系统方案选择 ............................................. 6 2.3角度的获取模块论证与选择 ..................................... 6 2.4步进电机及其驱动模块的选择 ................................... 7 2.5 AD/DA的选择 ................................................. 7 3 系统的硬件设计 .................................................... 7
3.1总体电路框图 ................................................. 7 图3-1 系统框图 ..................................................... 8
3.2系统电路与程序设计 ........................................... 9
3.2.1 STC89C52单片机最小系统 ................................ 9 3.2.2 PCF8591模块图如图3-2。 ............................... 10 3.3.3 模块芯片TB6560AHQ原理图如图3-3。 .................... 10 3.3.4 供电电源 .............................................. 11
4系统软件总体设计框图 ............................................. 13 5 测试方案与测试结果 ............................................... 13 6 总结 ............................................................. 15 参考文献 ........................................................... 16 附录 ............................................................... 17
简易旋转倒立摆及控制装置(C题)
【本科组】
1 设计任务及要求 1.1 设计任务
设计并制作一套简易旋转倒立摆及其控制装置。旋转倒立摆的结构如图1-1 所示。电动机 A 固定在支架 B 上,通过转轴 F 驱动旋转臂 C 旋转。摆杆 E 通过转轴 D 固定在旋转臂 C 的一端,当旋转臂 C 在电动机 A 驱动下作往复旋转运动时,带动摆杆 E 在垂直于旋转臂 C 的平面作自由旋转。
图1-1 旋转倒立摆结构示意图
1.2 基本要求
(1)摆杆从处于自然下垂状态(摆角 0°)开始,驱动电机带动旋转臂作往复旋转使摆杆摆动,并尽快使摆角达到或超过-60°~ +60°;
(2)从摆杆处于自然下垂状态开始,尽快增大摆杆的摆动幅度,直至完成圆周运动;
(3)在摆杆处于自然下垂状态下,外力拉起摆杆至接近 165°位置,外力撤除同时,启动控制旋转臂使摆杆保持倒立状态时间不少于 5s;期间 旋转臂的转动角度不大于 90°。
2主控制器件的论证与选择 2.1控制器选用
方案一: 采用ARM,运行速度快,引脚多,内部资源丰富,具有很高的运算速率,但是价格较高,对于初学者,ARM不易掌握.
方案二: 采用STC89C52单片机, 选用STC89C52单片机作为控制核心,它具有8k字节Flash,512字节RAM, 32 位I/O 口线,看门狗定时器,2 个数据指针,三个16 位 定时器/计数器,一个6向量2级中断结构,全双工串行口,片内晶振及时钟电路,且容易烧录,使用方便。 所以我们选用STC89V52作为主控芯片 2.2控制系统方案选择
方案一:采用在面包板上搭建简易单片机系统
在面包板上搭建单片机系统可以方便的对硬件做随时修改,也易于搭建,但是系统连线较多,不仅相互干扰,使电路杂乱无章,而且系统可靠性低。 方案二:自制单片机印刷电路板
自制印刷电路实现较为困难,实现周期长,此外也会花费较多的时间,影响整体设计进程。
方案三:采用单片机最小系统。
单片机最小系统,能明显减少外围电路的设计,降低系统设计的难度,非常适合本系统的设计。
综上所述,我们选择方案三。 2.3角度的获取模块论证与选择
方案一:采用加速度传感器
加速度传感器采用模拟量输出,需要放大电路及A/D完成角度的测量,测量精度高,但是摆杆上不易安装重物,且不易固定。
方案二:采用增量式光电旋转编码器
光电旋转编码器是一种角度(角速度)检测装置,它将输入给轴的角度量,利用光电转换原理转换成相应的电脉冲。旋转编码器具有体积小,精度高,工作可靠,接口数字化等优点。但是旋转编码器安装较为不便,增加了系统硬件电路设计的工作量。
方案三:采用电位器作为角度传感器
简易旋转倒立摆系统的角度测量也可采用可变电阻器。精密的可变电阻器具易获得、重复性高、分辨率高、高频响应特性好、易使用等特点。且电位器传感器结构简单,体积小,价格低廉,受环境因素影响小,性能稳定。
综合以上三种方案微调电位器可以很好地达到我们的要求,角度有效范围载33.3度左右,由于本课题精度不高,考虑带经济性和灵活性,我们选择方案三。
2.4步进电机及其驱动模块的选择
方案一:采用直流减速电机,转速较低,反应速度慢,但是驱动模块简单。
方案二:采用型号为57 步进电机,为两相四线步进电机,它的步距角仅为1.8°,扭矩为0.50N/m,有较高的空载启动频率,在十六细分后能实现 0.225°的步距角能够满足本系统的控制要求,驱动电路较复杂,用42/57专用驱动模块TB6560AHQ驱动,能满足要求,而驱动L298N模块功率较小,无法满足要求,易造成失步。
最终选定的步进电机为57步进电机,驱动电路模块选用TB6560AHQ模块。 2.5 AD/DA的选择
方案一:采用ADC0832
ADC0832为8位分辨率A/D转换芯片,其最高分辨可达256级,可以适应一般的模拟量转换要求。其内部电源输入与参考电压的复用,使得芯片的模拟电压输入在0~5V之间。芯片转换时间仅为32μS,据有双数据输出可作为数据校验,以减少数据误差,转换速度慢且稳定性能较差,而且占用I/O口多。
方案二: 采用PCF8591
PCF8591具有I2C总线结构的多通道8bits的逐次逼近型ADC和一个内置8bits单通道ADC,功能多,速度超快,功耗低,单电源供电,串行输入输出,节约I/O口资源,并能在一个处理系统中外接多个PCF8591,能进行更多更强的处理。 综上,从各方面考虑,我们选择方案二。 3 系统的硬件设计 3.1总体电路框图
为了使系统能够实现各种复杂的控制功能,本设计采用一种功能强大的、高速低功耗性价比高的单片机STC89C52完成对其他部分控制。本设计采用SV01A103AEA01R00 旋转角度传感器(旋转电位器)对摆杆的倾斜角度进行数据采集,通过PCF8591 D/A转换芯片将数据送入单片机,单片机通过数据分析控制TB6560AHQ驱动电路,进而控制步进电机使步进机旋转达到设定的位置,用数码管显示A/D的数据。总体框图如图3-1所示。
图3-1 系统框图
3.2系统电路与程序设计
3.2.1 STC89C52单片机最小系统
最小系统包括复位、按键 、显示和电源部分,而下载模块用单片机最小系统直接下载,减少了系统的浪费,而且防止连续的拔插单片机。STC89C52单片机最小系统如图3-4所示。
图3-4 最小系统
3.2.2 PCF8591模块图如图3-2。
图3-2 PCF8591模块图
3.3.3 模块芯片TB6560AHQ原理图如图3-3。
3.3.4 供电电源
图3-3 模块芯片TB6560AHQ原理图
由于需要驱动57步进电机,防止失步,其需要的功率较大,我们采用现有的直流稳压电源直接供电,电源模块的示意图如图3-5。为了达到较好的工作效果,我们选用兆信RXD-302-Ⅱ双路电源供电,具有很好的可靠性和灵活性,电压电流均可调,而且还在带一个5V电压输出端。
图3-5 电源电路
4系统软件总体设计框图 如图4-1所示。
图4-1 总体程序框图
5 测试方案与测试结果
测试结果(S)
这次设计的要求,所设计的系统要有较高的灵敏度和相应的转矩,这两样是最为重要的,我们所设计的系统,不能很好地满足要求,有待进一步改进
6 总结
经过四天三夜的辛勤努力,此次基于单片机为控制核心的简易旋转倒立摆的系统设计终于完成。通过合理的系统构建和软件编程,本系统也未能够完成题目的要求,实现摆杆的旋转及倒立,实际测试表明,所设计系统的稳定性有待改进。但由于时间紧,任务重,系统还有一些功能未能实现,比如摆杆在受到干扰后,能够及时恢复倒立状态。若经过改进,相信性能还会有进一步的提升。本次竞赛极大的锻炼了我们各方面的能力,虽然我们遇到了很多困难和障碍,但总体上成功与挫折交替,困难与希望并存,我们将继续努力争取更大的进步。
参考文献
[1]刘宝延.步进电机及其驱动控制系统[M].哈尔滨:哈尔滨工业大学出版社,1972.
[2]周航慈.单片机应用程序设计技术[M].北京:北京航空航天大学出版社,1991.
[3] 郁有文.传感器原理及工程应用[M].西安:西安电子科技大学出版,2008.
[4] 宋戈.51单片机应用开发范例大全[M].北京:人民邮电出版社,2010. [5]张毅刚.单片机原理及应用[M].北京:高等教育出版社,2009. [6] 吴建平.传感器原理及应用[M].北京:机械工业出版社,2009.
[7] 唐继贤.51单片机工程应用实例[M].北京:北京航空航天大学出版社,2009.
附录
第一部分
$NOMOD51
;------------------------------------------------------------------------------
; This file is part of the C51 Compiler package
; Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc.
;------------------------------------------------------------------------------
; STARTUP.A51: This code is executed after processor reset. ;
; To translate this file use A51 with the following invocation: ;
; A51 STARTUP.A51 ;
; To link the modified STARTUP.OBJ file to your application use the following ; BL51 invocation: ;
; BL51 , STARTUP.OBJ ;
;------------------------------------------------------------------------------ ;
; User-defined Power-On Initialization of Memory ;
; With the following EQU statements the initialization of memory ; at processor reset can be defined: ;
; ; the absolute start-address of IDATA memory is always 0 IDATALEN EQU 80H ; the length of IDATA memory in bytes. ;
XDATASTART EQU 0H ; the absolute start-address of XDATA memory XDATALEN EQU 0H ; the length of XDATA memory in bytes. ;
PDATASTART EQU 0H ; the absolute start-address of PDATA memory PDATALEN EQU 0H ; the length of PDATA memory in bytes. ;
; Notes: The IDATA space overlaps physically the DATA and BIT areas of the ; 8051 CPU. At minimum the memory space occupied from the C51 ; run-time routines must be set to zero.
;------------------------------------------------------------------------------
;
; Reentrant Stack Initilization ;
; The following EQU statements define the stack pointer for reentrant ; functions and initialized it: ;
; Stack Space for reentrant functions in the SMALL model.
IBPSTACK EQU 0 ; set to 1 if small reentrant is used. IBPSTACKTOP EQU 0FFH+1 ; set top of stack to highest location+1. ;
; Stack Space for reentrant functions in the LARGE model.
XBPSTACK EQU 0 ; set to 1 if large reentrant is used. XBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1. ;
; Stack Space for reentrant functions in the COMPACT model.
PBPSTACK EQU 0 ; set to 1 if compact reentrant is used. PBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1. ;
;------------------------------------------------------------------------------ ;
; Page Definition for Using the Compact Model with 64 KByte xdata RAM ;
; The following EQU statements define the xdata page used for pdata ; variables. The EQU PPAGE must conform with the PPAGE control used ; in the linker invocation. ;
PPAGEENABLE EQU 0 ; set to 1 if pdata object are used. ;
PPAGE EQU 0 ; define PPAGE number. ;
PPAGE_SFR DATA 0A0H ; SFR that supplies uppermost address byte ; (most 8051 variants use P2 as uppermost address byte) ;
;------------------------------------------------------------------------------
; Standard SFR Symbols ACC DATA 0E0H B DATA 0F0H SP DATA 81H DPL DATA 82H DPH DATA 83H
NAME ?C_STARTUP
?C_C51STARTUP SEGMENT CODE ?STACK SEGMENT IDATA
RSEG ?STACK DS 1
EXTRN CODE (?C_START) PUBLIC ?C_STARTUP
CSEG AT 0 ?C_STARTUP: LJMP STARTUP1
RSEG ?C_C51STARTUP
STARTUP1:
IF IDATALEN 0
MOV R0,#IDATALEN - 1 CLR A IDATALOOP: MOV @R0,A
DJNZ R0,IDATALOOP ENDIF
IF XDATALEN 0
MOV DPTR,#XDATASTART MOV R7,#LOW (XDATALEN) IF (LOW (XDATALEN)) 0
MOV R6,#(HIGH (XDATALEN)) +1 ELSE
MOV R6,#HIGH (XDATALEN) ENDIF
CLR A
XDATALOOP: MOVX @DPTR,A INC DPTR
DJNZ R7,XDATALOOP DJNZ R6,XDATALOOP ENDIF
IF PPAGEENABLE 0
MOV PPAGE_SFR,#PPAGE ENDIF
IF PDATALEN 0
MOV R0,#LOW (PDATASTART) MOV R7,#LOW (PDATALEN) CLR A PDATALOOP: MOVX @R0,A INC R0
DJNZ R7,PDATALOOP ENDIF
IF IBPSTACK 0 EXTRN DATA (?C_IBP)
MOV ?C_IBP,#LOW IBPSTACKTOP ENDIF
IF XBPSTACK 0 EXTRN DATA (?C_XBP)
MOV ?C_XBP,#HIGH XBPSTACKTOP MOV ?C_XBP+1,#LOW XBPSTACKTOP ENDIF
IF PBPSTACK 0 EXTRN DATA (?C_PBP)
MOV ?C_PBP,#LOW PBPSTACKTOP ENDIF
MOV SP,#?STACK-1
; This code is required if you use L51_BANK.A51 with Banking Mode 4 ; EXTRN CODE (?B_SWITCH0)
; CALL ?B_SWITCH0 ; init bank mechanism to code bank 0 LJMP ?C_START
END 第二部分
/**********************************************************************************
* 标题: ************* DA-AD试验******************* *
*
/******************************主程*****************************************************/ 序
#include #include
#define PCF8591 0x90 //PCF8591 地址
//else IO
sbit LS138A=P2^2; sbit LS138B=P2^3; sbit LS138C=P2^4;
sbit first=P2^5; //按键1 模式1选择 sbit second=P2^6; //按键2 模式2选择 sbit third=P2^7; //按键3 模式3选择 sbit fouth=P3^0; //按键4 模式4选择 sbit fith=P3^1; //按键5 模式清零选择 sbit shi_neng=P1^0; // 使能控制位 sbit fang_xiang=P1^1;// 旋转方向控制位 sbit mai_chong=P1^2; // 脉冲控制位
//此表为 LED 的字模, 共阴数码管 0-9 - unsigned char code {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; unsigned char AD_CHANNEL;
unsigned long xdata LedOut[8]; signed int D[32];
unsigned int ad=0,ad0=0; unsigned int ms15=0;
unsigned char i=0,a=0,b=0,c=0,e=0; unsigned char xiang; signed char n=0; unsigned int xx(); void round(); void system() {
TMOD=0x01;
TH0=(65535-2000)/256; TL0=(65535-2000)%256; IE=0x8a; IT0=0; TR0=1; }
Disp_Tab[] =
/******************************************************************* DAC 变换, 转化函数
*******************************************************************/ bit DACconversion(unsigned char sla,unsigned char c, unsigned char Val) {
Start_I2c(); //启动总线
SendByte(sla); //发送器件地址
if(ack==0)return(0);
SendByte(c); //发送控制字节
if(ack==0)return(0);
SendByte(Val); //发送DAC的数值
if(ack==0)return(0);
Stop_I2c(); //结束总线
return(1);
}
/******************************************************************* ADC发送字节[命令]数据函数
*******************************************************************/ bit ISendByte(unsigned char sla,unsigned char c)
{
Start_I2c(); //启动总线
SendByte(sla); //发送器件地址
if(ack==0)return(0);
SendByte(c); //发送数据
if(ack==0)return(0);
Stop_I2c(); //结束总线
return(1);
}
/******************************************************************* ADC读字节数据函数
*******************************************************************/ unsigned char IRcvByte(unsigned char sla)
{ unsigned char c;
Start_I2c(); //启动总线
SendByte(sla+1); //发送器件地址
if(ack==0)return(0);
c=RcvByte(); //读取数据0
Ack_I2c(1); //发送非就答位
Stop_I2c(); //结束总线
return(c);
}
//******************************************************************/ main()
{
system();
shi_neng=0;
first=1;
second=1;
third=1;
fouth=1;
fith=1;
while(1)
{/********以下AD-DA处理*************/
switch(AD_CHANNEL)
{
case 0: ISendByte(PCF8591,0x41);
D[0]=IRcvByte(PCF8591)*2; //ADC0 模数转换1 放大2倍显示 break;
case 1: ISendByte(PCF8591,0x42);
D[1]=IRcvByte(PCF8591)*2; //ADC1 模数转换2
break;
case 2: ISendByte(PCF8591,0x43);
D[2]=IRcvByte(PCF8591)*2; //ADC2 模数转换3
break;
case 3: ISendByte(PCF8591,0x40);
D[3]=IRcvByte(PCF8591)*2; //ADC3 模数转换4
break;
case 4: DACconversion(PCF8591,0x40, D[4]/2); //DAC 数模转换
break;
}
// D[4]=400; //数字--->>模拟输出
D[4]=D[0]; // 把模拟输入 采样的信号 通过数模转换输出
if(++AD_CHANNEL>4) AD_CHANNEL=0;
ad=D[0]*10;
xiang=xx();
if(first==0)a=1,b=0,c=0,e=0;
if(second==0)b=1,a=0,c=0,e=0;
if(third==0)c=1,a=0,b=0,e=0;
if(fouth==0)e=1,a=0,b=0,c=0;
if(fith==0)a=0,b=0,c=0,e=0;
if(a==1)
{
if(xiang==0)
{
if(ad=4250)
n=1;
if(ad>=0&&ad
n=-1;
}
else n=0;
}
if(b==1)
{
if(xiang==0)
{
if(ad
n=-1;
if(ad>=ad0)
n=1;
}
else
round();
}
if(c==1)
{
if(ad>=2440&&ad
n=1;
if(ad>=2720&&ad
n=-1;
}
if(e==1)
{
if(ad
{
if(ad>=2380&&ad
n=1;
if(ad>=2720&&ad
n=-1;
}
else round();
}
ad0=ad;
/********以下将AD的值送到LED数码管显示*************/
LedOut[0]=Disp_Tab[D[0]%10000/1000];
LedOut[1]=Disp_Tab[D[0]%1000/100];
LedOut[2]=Disp_Tab[D[0]%100/10]|0x80;
LedOut[3]=Disp_Tab[D[0]%10];
}
}
unsigned int xx() //摆杆象限检测
{
unsigned char Q;
if(ad>20&&ad
{
Q=1;
}
else if(ad>1360&&ad
{
Q=2;
}
else if(ad>2380&&ad
{
Q=3;
}
else if(ad>2680&&ad
{
Q=4;
}
else if(ad>2980&&ad
{
Q=5;
}
else if(ad>4040&&ad
{
Q=6;
}
else Q=0;
return(Q);
}
void round() //状态检测及相应操作
{
if(ad>ad0&&xiang==6)
{
n=1;
}
else if(ad>ad0&&xiang==5)
{
n=1;
}
else if(ad
{
n=-1;
}
else if(ad>ad0&&xiang==3)
{
n=1;
}
else if(ad
{
n=-1;
}
else if(ad
{
n=-1;
}
else n=0;
}
void T0ZD(void) interrupt 1 using 2
{
TH0=(65535-2000)/256;//250us
TL0=(65535-2000)%256;
//us50++;
ms15++;
mai_chong=1;
P0 = LedOut[i];
switch(i) //使用switch 语句控制138译码器
{
case 0:LS138A=0; LS138B=0; LS138C=0; break;
case 1:LS138A=1; LS138B=0; LS138C=0; break;
case 2:LS138A=0; LS138B=1; LS138C=0; break;
case 3:LS138A=1; LS138B=1; LS138C=0; break;
case 4:LS138A=0; LS138B=0; LS138C=1; break;
case 5:LS138A=1; LS138B=0; LS138C=1; break;
case 6:LS138A=0; LS138B=1; LS138C=1; break;
case 7:LS138A=1; LS138B=1; LS138C=1; break;
}
if(++i>7) i=0;
if(ms15>5)
{
ms15=0;
if(n>0) //电机反转程序
{
fang_xiang=1;
mai_chong=0;
}
if(n
{
fang_xiang=0;
mai_chong=0;
}
if(n==0) //电机锁定程序
{
mai_chong=1;
}
}
}
第三部分
/*************************此部分为I2C总线的驱动程序,用来读取adda数据*************************************/
#include
#include
#include
#define NOP() _nop_() /* 定义空指令 */
#define _Nop() _nop_() /*定义空指令*/
sbit SCL=P2^1; //I2C 时钟
sbit SDA=P2^0; //I2C 数据
bit ack; /*应答标志位*/
/******************************************************************* 起动总线函数
函数原型: void Start_I2c();
功能: 启动I2C总线,即发送I2C起始条件.
********************************************************************/ void Start_I2c()
{
SDA=1; /*发送起始条件的数据信号*/
_Nop();
SCL=1;
_Nop(); /*起始条件建立时间大于4.7us,延时*/
_Nop();
_Nop();
_Nop();
_Nop();
SDA=0; /*发送起始信号*/
_Nop(); /* 起始条件锁定时间大于4μs*/
_Nop();
_Nop();
_Nop();
_Nop();
SCL=0; /*钳住I2C总线,准备发送或接收数据 */
_Nop();
_Nop();
}
/******************************************************************* 结束总线函数
函数原型: void Stop_I2c();
功能: 结束I2C总线,即发送I2C结束条件.
********************************************************************/ void Stop_I2c()
{
SDA=0; /*发送结束条件的数据信号*/
_Nop(); /*发送结束条件的时钟信号*/
SCL=1; /*结束条件建立时间大于4μs*/
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SDA=1; /*发送I2C总线结束信号*/
_Nop();
_Nop();
_Nop();
_Nop();
}
/******************************************************************* 字节数据发送函数
函数原型: void SendByte(UCHAR c);
功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对 此状态位进行操作.(不应答或非应答都使ack=0)
发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
********************************************************************/ void SendByte(unsigned char c)
{
unsigned char BitCnt;
for(BitCnt=0;BitCnt
{
if((c
else SDA=0;
_Nop();
SCL=1; /*置时钟线为高,通知被控器开始接收数据位*/ _Nop();
_Nop(); /*保证时钟高电平周期大于4μs*/
_Nop();
_Nop();
_Nop();
SCL=0;
}
_Nop();
_Nop();
SDA=1; /*8位发送完后释放数据线,准备接收应答位*/ _Nop();
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();
if(SDA==1)ack=0;
else ack=1; /*判断是否接收到应答信号*/
SCL=0;
_Nop();
_Nop();
}
/******************************************************************* 字节数据接收函数
函数原型: UCHAR RcvByte();
功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号), 发完后请用应答函数应答从机。
********************************************************************/ unsigned char RcvByte()
{
unsigned char retc;
unsigned char BitCnt;
retc=0;
/*置数据线为输入方式*/
for(BitCnt=0;BitCnt
{
SDA=1;
_Nop();
SCL=0; /*置时钟线为低,准备接收数据位*/ _Nop();
_Nop(); /*时钟低电平周期大于4.7μs*/ _Nop();
_Nop();
_Nop();
SCL=1; /*置时钟线为高使数据线上数据有效*/ _Nop();
_Nop();
retc=retc
if(SDA==1)retc=retc+1; /*读数据位,接收的数据位放入retc中 */ _Nop();
_Nop();
SCL=0;
_Nop();
_Nop();
}
return(retc);
}
/******************************************************************** 应答子函数
函数原型: void Ack_I2c(bit a);
功能: 主控器进行应答信号(可以是应答或非应答信号,由位参数a决定) ********************************************************************/ void Ack_I2c(bit a)
{
if(a==0)SDA=0; /*在此发出应答或非应答信号 */ else SDA=1;
_Nop();
_Nop();
_Nop();
SCL=1;
_Nop();
_Nop(); /*时钟低电平周期大于4μs*/
_Nop();
_Nop();
_Nop();
SCL=0; /*清时钟线,钳住I2C总线以便继续接收*/ _Nop();
_Nop(); }
32
2013年全国大学生电子设计竞赛
简易旋转倒立摆及控制装置(C题)
【本科组】
2013年9月7
日
摘要
本题要求设计一个简易旋转倒立摆及控制系统,其中角度传感器、步进电机和单片机
890C521是系统核心部件。系统接收角度传感器反馈的信号,通过PCF8591将接收的信号转换成数字信号,将数值送入单片机中进行计算,可得出摆杆的位置,进而单片机控制步进电机,对摆杆进行控制,达到所要的旋转或者倒立的控制目标。
关键词:简易旋转倒立摆
步进电机
单片机 角度传感器
目录
1 设计任务及要求 .................................................... 5
1.1 设计任务 .................................................... 5 1.2 基本要求 ................................................... 5 2主控制器件的论证与选择 ............................................ 6
2.1控制器选用 ................................................... 6 2.2控制系统方案选择 ............................................. 6 2.3角度的获取模块论证与选择 ..................................... 6 2.4步进电机及其驱动模块的选择 ................................... 7 2.5 AD/DA的选择 ................................................. 7 3 系统的硬件设计 .................................................... 7
3.1总体电路框图 ................................................. 7 图3-1 系统框图 ..................................................... 8
3.2系统电路与程序设计 ........................................... 9
3.2.1 STC89C52单片机最小系统 ................................ 9 3.2.2 PCF8591模块图如图3-2。 ............................... 10 3.3.3 模块芯片TB6560AHQ原理图如图3-3。 .................... 10 3.3.4 供电电源 .............................................. 11
4系统软件总体设计框图 ............................................. 13 5 测试方案与测试结果 ............................................... 13 6 总结 ............................................................. 15 参考文献 ........................................................... 16 附录 ............................................................... 17
简易旋转倒立摆及控制装置(C题)
【本科组】
1 设计任务及要求 1.1 设计任务
设计并制作一套简易旋转倒立摆及其控制装置。旋转倒立摆的结构如图1-1 所示。电动机 A 固定在支架 B 上,通过转轴 F 驱动旋转臂 C 旋转。摆杆 E 通过转轴 D 固定在旋转臂 C 的一端,当旋转臂 C 在电动机 A 驱动下作往复旋转运动时,带动摆杆 E 在垂直于旋转臂 C 的平面作自由旋转。
图1-1 旋转倒立摆结构示意图
1.2 基本要求
(1)摆杆从处于自然下垂状态(摆角 0°)开始,驱动电机带动旋转臂作往复旋转使摆杆摆动,并尽快使摆角达到或超过-60°~ +60°;
(2)从摆杆处于自然下垂状态开始,尽快增大摆杆的摆动幅度,直至完成圆周运动;
(3)在摆杆处于自然下垂状态下,外力拉起摆杆至接近 165°位置,外力撤除同时,启动控制旋转臂使摆杆保持倒立状态时间不少于 5s;期间 旋转臂的转动角度不大于 90°。
2主控制器件的论证与选择 2.1控制器选用
方案一: 采用ARM,运行速度快,引脚多,内部资源丰富,具有很高的运算速率,但是价格较高,对于初学者,ARM不易掌握.
方案二: 采用STC89C52单片机, 选用STC89C52单片机作为控制核心,它具有8k字节Flash,512字节RAM, 32 位I/O 口线,看门狗定时器,2 个数据指针,三个16 位 定时器/计数器,一个6向量2级中断结构,全双工串行口,片内晶振及时钟电路,且容易烧录,使用方便。 所以我们选用STC89V52作为主控芯片 2.2控制系统方案选择
方案一:采用在面包板上搭建简易单片机系统
在面包板上搭建单片机系统可以方便的对硬件做随时修改,也易于搭建,但是系统连线较多,不仅相互干扰,使电路杂乱无章,而且系统可靠性低。 方案二:自制单片机印刷电路板
自制印刷电路实现较为困难,实现周期长,此外也会花费较多的时间,影响整体设计进程。
方案三:采用单片机最小系统。
单片机最小系统,能明显减少外围电路的设计,降低系统设计的难度,非常适合本系统的设计。
综上所述,我们选择方案三。 2.3角度的获取模块论证与选择
方案一:采用加速度传感器
加速度传感器采用模拟量输出,需要放大电路及A/D完成角度的测量,测量精度高,但是摆杆上不易安装重物,且不易固定。
方案二:采用增量式光电旋转编码器
光电旋转编码器是一种角度(角速度)检测装置,它将输入给轴的角度量,利用光电转换原理转换成相应的电脉冲。旋转编码器具有体积小,精度高,工作可靠,接口数字化等优点。但是旋转编码器安装较为不便,增加了系统硬件电路设计的工作量。
方案三:采用电位器作为角度传感器
简易旋转倒立摆系统的角度测量也可采用可变电阻器。精密的可变电阻器具易获得、重复性高、分辨率高、高频响应特性好、易使用等特点。且电位器传感器结构简单,体积小,价格低廉,受环境因素影响小,性能稳定。
综合以上三种方案微调电位器可以很好地达到我们的要求,角度有效范围载33.3度左右,由于本课题精度不高,考虑带经济性和灵活性,我们选择方案三。
2.4步进电机及其驱动模块的选择
方案一:采用直流减速电机,转速较低,反应速度慢,但是驱动模块简单。
方案二:采用型号为57 步进电机,为两相四线步进电机,它的步距角仅为1.8°,扭矩为0.50N/m,有较高的空载启动频率,在十六细分后能实现 0.225°的步距角能够满足本系统的控制要求,驱动电路较复杂,用42/57专用驱动模块TB6560AHQ驱动,能满足要求,而驱动L298N模块功率较小,无法满足要求,易造成失步。
最终选定的步进电机为57步进电机,驱动电路模块选用TB6560AHQ模块。 2.5 AD/DA的选择
方案一:采用ADC0832
ADC0832为8位分辨率A/D转换芯片,其最高分辨可达256级,可以适应一般的模拟量转换要求。其内部电源输入与参考电压的复用,使得芯片的模拟电压输入在0~5V之间。芯片转换时间仅为32μS,据有双数据输出可作为数据校验,以减少数据误差,转换速度慢且稳定性能较差,而且占用I/O口多。
方案二: 采用PCF8591
PCF8591具有I2C总线结构的多通道8bits的逐次逼近型ADC和一个内置8bits单通道ADC,功能多,速度超快,功耗低,单电源供电,串行输入输出,节约I/O口资源,并能在一个处理系统中外接多个PCF8591,能进行更多更强的处理。 综上,从各方面考虑,我们选择方案二。 3 系统的硬件设计 3.1总体电路框图
为了使系统能够实现各种复杂的控制功能,本设计采用一种功能强大的、高速低功耗性价比高的单片机STC89C52完成对其他部分控制。本设计采用SV01A103AEA01R00 旋转角度传感器(旋转电位器)对摆杆的倾斜角度进行数据采集,通过PCF8591 D/A转换芯片将数据送入单片机,单片机通过数据分析控制TB6560AHQ驱动电路,进而控制步进电机使步进机旋转达到设定的位置,用数码管显示A/D的数据。总体框图如图3-1所示。
图3-1 系统框图
3.2系统电路与程序设计
3.2.1 STC89C52单片机最小系统
最小系统包括复位、按键 、显示和电源部分,而下载模块用单片机最小系统直接下载,减少了系统的浪费,而且防止连续的拔插单片机。STC89C52单片机最小系统如图3-4所示。
图3-4 最小系统
3.2.2 PCF8591模块图如图3-2。
图3-2 PCF8591模块图
3.3.3 模块芯片TB6560AHQ原理图如图3-3。
3.3.4 供电电源
图3-3 模块芯片TB6560AHQ原理图
由于需要驱动57步进电机,防止失步,其需要的功率较大,我们采用现有的直流稳压电源直接供电,电源模块的示意图如图3-5。为了达到较好的工作效果,我们选用兆信RXD-302-Ⅱ双路电源供电,具有很好的可靠性和灵活性,电压电流均可调,而且还在带一个5V电压输出端。
图3-5 电源电路
4系统软件总体设计框图 如图4-1所示。
图4-1 总体程序框图
5 测试方案与测试结果
测试结果(S)
这次设计的要求,所设计的系统要有较高的灵敏度和相应的转矩,这两样是最为重要的,我们所设计的系统,不能很好地满足要求,有待进一步改进
6 总结
经过四天三夜的辛勤努力,此次基于单片机为控制核心的简易旋转倒立摆的系统设计终于完成。通过合理的系统构建和软件编程,本系统也未能够完成题目的要求,实现摆杆的旋转及倒立,实际测试表明,所设计系统的稳定性有待改进。但由于时间紧,任务重,系统还有一些功能未能实现,比如摆杆在受到干扰后,能够及时恢复倒立状态。若经过改进,相信性能还会有进一步的提升。本次竞赛极大的锻炼了我们各方面的能力,虽然我们遇到了很多困难和障碍,但总体上成功与挫折交替,困难与希望并存,我们将继续努力争取更大的进步。
参考文献
[1]刘宝延.步进电机及其驱动控制系统[M].哈尔滨:哈尔滨工业大学出版社,1972.
[2]周航慈.单片机应用程序设计技术[M].北京:北京航空航天大学出版社,1991.
[3] 郁有文.传感器原理及工程应用[M].西安:西安电子科技大学出版,2008.
[4] 宋戈.51单片机应用开发范例大全[M].北京:人民邮电出版社,2010. [5]张毅刚.单片机原理及应用[M].北京:高等教育出版社,2009. [6] 吴建平.传感器原理及应用[M].北京:机械工业出版社,2009.
[7] 唐继贤.51单片机工程应用实例[M].北京:北京航空航天大学出版社,2009.
附录
第一部分
$NOMOD51
;------------------------------------------------------------------------------
; This file is part of the C51 Compiler package
; Copyright (c) 1988-2002 Keil Elektronik GmbH and Keil Software, Inc.
;------------------------------------------------------------------------------
; STARTUP.A51: This code is executed after processor reset. ;
; To translate this file use A51 with the following invocation: ;
; A51 STARTUP.A51 ;
; To link the modified STARTUP.OBJ file to your application use the following ; BL51 invocation: ;
; BL51 , STARTUP.OBJ ;
;------------------------------------------------------------------------------ ;
; User-defined Power-On Initialization of Memory ;
; With the following EQU statements the initialization of memory ; at processor reset can be defined: ;
; ; the absolute start-address of IDATA memory is always 0 IDATALEN EQU 80H ; the length of IDATA memory in bytes. ;
XDATASTART EQU 0H ; the absolute start-address of XDATA memory XDATALEN EQU 0H ; the length of XDATA memory in bytes. ;
PDATASTART EQU 0H ; the absolute start-address of PDATA memory PDATALEN EQU 0H ; the length of PDATA memory in bytes. ;
; Notes: The IDATA space overlaps physically the DATA and BIT areas of the ; 8051 CPU. At minimum the memory space occupied from the C51 ; run-time routines must be set to zero.
;------------------------------------------------------------------------------
;
; Reentrant Stack Initilization ;
; The following EQU statements define the stack pointer for reentrant ; functions and initialized it: ;
; Stack Space for reentrant functions in the SMALL model.
IBPSTACK EQU 0 ; set to 1 if small reentrant is used. IBPSTACKTOP EQU 0FFH+1 ; set top of stack to highest location+1. ;
; Stack Space for reentrant functions in the LARGE model.
XBPSTACK EQU 0 ; set to 1 if large reentrant is used. XBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1. ;
; Stack Space for reentrant functions in the COMPACT model.
PBPSTACK EQU 0 ; set to 1 if compact reentrant is used. PBPSTACKTOP EQU 0FFFFH+1; set top of stack to highest location+1. ;
;------------------------------------------------------------------------------ ;
; Page Definition for Using the Compact Model with 64 KByte xdata RAM ;
; The following EQU statements define the xdata page used for pdata ; variables. The EQU PPAGE must conform with the PPAGE control used ; in the linker invocation. ;
PPAGEENABLE EQU 0 ; set to 1 if pdata object are used. ;
PPAGE EQU 0 ; define PPAGE number. ;
PPAGE_SFR DATA 0A0H ; SFR that supplies uppermost address byte ; (most 8051 variants use P2 as uppermost address byte) ;
;------------------------------------------------------------------------------
; Standard SFR Symbols ACC DATA 0E0H B DATA 0F0H SP DATA 81H DPL DATA 82H DPH DATA 83H
NAME ?C_STARTUP
?C_C51STARTUP SEGMENT CODE ?STACK SEGMENT IDATA
RSEG ?STACK DS 1
EXTRN CODE (?C_START) PUBLIC ?C_STARTUP
CSEG AT 0 ?C_STARTUP: LJMP STARTUP1
RSEG ?C_C51STARTUP
STARTUP1:
IF IDATALEN 0
MOV R0,#IDATALEN - 1 CLR A IDATALOOP: MOV @R0,A
DJNZ R0,IDATALOOP ENDIF
IF XDATALEN 0
MOV DPTR,#XDATASTART MOV R7,#LOW (XDATALEN) IF (LOW (XDATALEN)) 0
MOV R6,#(HIGH (XDATALEN)) +1 ELSE
MOV R6,#HIGH (XDATALEN) ENDIF
CLR A
XDATALOOP: MOVX @DPTR,A INC DPTR
DJNZ R7,XDATALOOP DJNZ R6,XDATALOOP ENDIF
IF PPAGEENABLE 0
MOV PPAGE_SFR,#PPAGE ENDIF
IF PDATALEN 0
MOV R0,#LOW (PDATASTART) MOV R7,#LOW (PDATALEN) CLR A PDATALOOP: MOVX @R0,A INC R0
DJNZ R7,PDATALOOP ENDIF
IF IBPSTACK 0 EXTRN DATA (?C_IBP)
MOV ?C_IBP,#LOW IBPSTACKTOP ENDIF
IF XBPSTACK 0 EXTRN DATA (?C_XBP)
MOV ?C_XBP,#HIGH XBPSTACKTOP MOV ?C_XBP+1,#LOW XBPSTACKTOP ENDIF
IF PBPSTACK 0 EXTRN DATA (?C_PBP)
MOV ?C_PBP,#LOW PBPSTACKTOP ENDIF
MOV SP,#?STACK-1
; This code is required if you use L51_BANK.A51 with Banking Mode 4 ; EXTRN CODE (?B_SWITCH0)
; CALL ?B_SWITCH0 ; init bank mechanism to code bank 0 LJMP ?C_START
END 第二部分
/**********************************************************************************
* 标题: ************* DA-AD试验******************* *
*
/******************************主程*****************************************************/ 序
#include #include
#define PCF8591 0x90 //PCF8591 地址
//else IO
sbit LS138A=P2^2; sbit LS138B=P2^3; sbit LS138C=P2^4;
sbit first=P2^5; //按键1 模式1选择 sbit second=P2^6; //按键2 模式2选择 sbit third=P2^7; //按键3 模式3选择 sbit fouth=P3^0; //按键4 模式4选择 sbit fith=P3^1; //按键5 模式清零选择 sbit shi_neng=P1^0; // 使能控制位 sbit fang_xiang=P1^1;// 旋转方向控制位 sbit mai_chong=P1^2; // 脉冲控制位
//此表为 LED 的字模, 共阴数码管 0-9 - unsigned char code {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f}; unsigned char AD_CHANNEL;
unsigned long xdata LedOut[8]; signed int D[32];
unsigned int ad=0,ad0=0; unsigned int ms15=0;
unsigned char i=0,a=0,b=0,c=0,e=0; unsigned char xiang; signed char n=0; unsigned int xx(); void round(); void system() {
TMOD=0x01;
TH0=(65535-2000)/256; TL0=(65535-2000)%256; IE=0x8a; IT0=0; TR0=1; }
Disp_Tab[] =
/******************************************************************* DAC 变换, 转化函数
*******************************************************************/ bit DACconversion(unsigned char sla,unsigned char c, unsigned char Val) {
Start_I2c(); //启动总线
SendByte(sla); //发送器件地址
if(ack==0)return(0);
SendByte(c); //发送控制字节
if(ack==0)return(0);
SendByte(Val); //发送DAC的数值
if(ack==0)return(0);
Stop_I2c(); //结束总线
return(1);
}
/******************************************************************* ADC发送字节[命令]数据函数
*******************************************************************/ bit ISendByte(unsigned char sla,unsigned char c)
{
Start_I2c(); //启动总线
SendByte(sla); //发送器件地址
if(ack==0)return(0);
SendByte(c); //发送数据
if(ack==0)return(0);
Stop_I2c(); //结束总线
return(1);
}
/******************************************************************* ADC读字节数据函数
*******************************************************************/ unsigned char IRcvByte(unsigned char sla)
{ unsigned char c;
Start_I2c(); //启动总线
SendByte(sla+1); //发送器件地址
if(ack==0)return(0);
c=RcvByte(); //读取数据0
Ack_I2c(1); //发送非就答位
Stop_I2c(); //结束总线
return(c);
}
//******************************************************************/ main()
{
system();
shi_neng=0;
first=1;
second=1;
third=1;
fouth=1;
fith=1;
while(1)
{/********以下AD-DA处理*************/
switch(AD_CHANNEL)
{
case 0: ISendByte(PCF8591,0x41);
D[0]=IRcvByte(PCF8591)*2; //ADC0 模数转换1 放大2倍显示 break;
case 1: ISendByte(PCF8591,0x42);
D[1]=IRcvByte(PCF8591)*2; //ADC1 模数转换2
break;
case 2: ISendByte(PCF8591,0x43);
D[2]=IRcvByte(PCF8591)*2; //ADC2 模数转换3
break;
case 3: ISendByte(PCF8591,0x40);
D[3]=IRcvByte(PCF8591)*2; //ADC3 模数转换4
break;
case 4: DACconversion(PCF8591,0x40, D[4]/2); //DAC 数模转换
break;
}
// D[4]=400; //数字--->>模拟输出
D[4]=D[0]; // 把模拟输入 采样的信号 通过数模转换输出
if(++AD_CHANNEL>4) AD_CHANNEL=0;
ad=D[0]*10;
xiang=xx();
if(first==0)a=1,b=0,c=0,e=0;
if(second==0)b=1,a=0,c=0,e=0;
if(third==0)c=1,a=0,b=0,e=0;
if(fouth==0)e=1,a=0,b=0,c=0;
if(fith==0)a=0,b=0,c=0,e=0;
if(a==1)
{
if(xiang==0)
{
if(ad=4250)
n=1;
if(ad>=0&&ad
n=-1;
}
else n=0;
}
if(b==1)
{
if(xiang==0)
{
if(ad
n=-1;
if(ad>=ad0)
n=1;
}
else
round();
}
if(c==1)
{
if(ad>=2440&&ad
n=1;
if(ad>=2720&&ad
n=-1;
}
if(e==1)
{
if(ad
{
if(ad>=2380&&ad
n=1;
if(ad>=2720&&ad
n=-1;
}
else round();
}
ad0=ad;
/********以下将AD的值送到LED数码管显示*************/
LedOut[0]=Disp_Tab[D[0]%10000/1000];
LedOut[1]=Disp_Tab[D[0]%1000/100];
LedOut[2]=Disp_Tab[D[0]%100/10]|0x80;
LedOut[3]=Disp_Tab[D[0]%10];
}
}
unsigned int xx() //摆杆象限检测
{
unsigned char Q;
if(ad>20&&ad
{
Q=1;
}
else if(ad>1360&&ad
{
Q=2;
}
else if(ad>2380&&ad
{
Q=3;
}
else if(ad>2680&&ad
{
Q=4;
}
else if(ad>2980&&ad
{
Q=5;
}
else if(ad>4040&&ad
{
Q=6;
}
else Q=0;
return(Q);
}
void round() //状态检测及相应操作
{
if(ad>ad0&&xiang==6)
{
n=1;
}
else if(ad>ad0&&xiang==5)
{
n=1;
}
else if(ad
{
n=-1;
}
else if(ad>ad0&&xiang==3)
{
n=1;
}
else if(ad
{
n=-1;
}
else if(ad
{
n=-1;
}
else n=0;
}
void T0ZD(void) interrupt 1 using 2
{
TH0=(65535-2000)/256;//250us
TL0=(65535-2000)%256;
//us50++;
ms15++;
mai_chong=1;
P0 = LedOut[i];
switch(i) //使用switch 语句控制138译码器
{
case 0:LS138A=0; LS138B=0; LS138C=0; break;
case 1:LS138A=1; LS138B=0; LS138C=0; break;
case 2:LS138A=0; LS138B=1; LS138C=0; break;
case 3:LS138A=1; LS138B=1; LS138C=0; break;
case 4:LS138A=0; LS138B=0; LS138C=1; break;
case 5:LS138A=1; LS138B=0; LS138C=1; break;
case 6:LS138A=0; LS138B=1; LS138C=1; break;
case 7:LS138A=1; LS138B=1; LS138C=1; break;
}
if(++i>7) i=0;
if(ms15>5)
{
ms15=0;
if(n>0) //电机反转程序
{
fang_xiang=1;
mai_chong=0;
}
if(n
{
fang_xiang=0;
mai_chong=0;
}
if(n==0) //电机锁定程序
{
mai_chong=1;
}
}
}
第三部分
/*************************此部分为I2C总线的驱动程序,用来读取adda数据*************************************/
#include
#include
#include
#define NOP() _nop_() /* 定义空指令 */
#define _Nop() _nop_() /*定义空指令*/
sbit SCL=P2^1; //I2C 时钟
sbit SDA=P2^0; //I2C 数据
bit ack; /*应答标志位*/
/******************************************************************* 起动总线函数
函数原型: void Start_I2c();
功能: 启动I2C总线,即发送I2C起始条件.
********************************************************************/ void Start_I2c()
{
SDA=1; /*发送起始条件的数据信号*/
_Nop();
SCL=1;
_Nop(); /*起始条件建立时间大于4.7us,延时*/
_Nop();
_Nop();
_Nop();
_Nop();
SDA=0; /*发送起始信号*/
_Nop(); /* 起始条件锁定时间大于4μs*/
_Nop();
_Nop();
_Nop();
_Nop();
SCL=0; /*钳住I2C总线,准备发送或接收数据 */
_Nop();
_Nop();
}
/******************************************************************* 结束总线函数
函数原型: void Stop_I2c();
功能: 结束I2C总线,即发送I2C结束条件.
********************************************************************/ void Stop_I2c()
{
SDA=0; /*发送结束条件的数据信号*/
_Nop(); /*发送结束条件的时钟信号*/
SCL=1; /*结束条件建立时间大于4μs*/
_Nop();
_Nop();
_Nop();
_Nop();
_Nop();
SDA=1; /*发送I2C总线结束信号*/
_Nop();
_Nop();
_Nop();
_Nop();
}
/******************************************************************* 字节数据发送函数
函数原型: void SendByte(UCHAR c);
功能: 将数据c发送出去,可以是地址,也可以是数据,发完后等待应答,并对 此状态位进行操作.(不应答或非应答都使ack=0)
发送数据正常,ack=1; ack=0表示被控器无应答或损坏。
********************************************************************/ void SendByte(unsigned char c)
{
unsigned char BitCnt;
for(BitCnt=0;BitCnt
{
if((c
else SDA=0;
_Nop();
SCL=1; /*置时钟线为高,通知被控器开始接收数据位*/ _Nop();
_Nop(); /*保证时钟高电平周期大于4μs*/
_Nop();
_Nop();
_Nop();
SCL=0;
}
_Nop();
_Nop();
SDA=1; /*8位发送完后释放数据线,准备接收应答位*/ _Nop();
_Nop();
SCL=1;
_Nop();
_Nop();
_Nop();
if(SDA==1)ack=0;
else ack=1; /*判断是否接收到应答信号*/
SCL=0;
_Nop();
_Nop();
}
/******************************************************************* 字节数据接收函数
函数原型: UCHAR RcvByte();
功能: 用来接收从器件传来的数据,并判断总线错误(不发应答信号), 发完后请用应答函数应答从机。
********************************************************************/ unsigned char RcvByte()
{
unsigned char retc;
unsigned char BitCnt;
retc=0;
/*置数据线为输入方式*/
for(BitCnt=0;BitCnt
{
SDA=1;
_Nop();
SCL=0; /*置时钟线为低,准备接收数据位*/ _Nop();
_Nop(); /*时钟低电平周期大于4.7μs*/ _Nop();
_Nop();
_Nop();
SCL=1; /*置时钟线为高使数据线上数据有效*/ _Nop();
_Nop();
retc=retc
if(SDA==1)retc=retc+1; /*读数据位,接收的数据位放入retc中 */ _Nop();
_Nop();
SCL=0;
_Nop();
_Nop();
}
return(retc);
}
/******************************************************************** 应答子函数
函数原型: void Ack_I2c(bit a);
功能: 主控器进行应答信号(可以是应答或非应答信号,由位参数a决定) ********************************************************************/ void Ack_I2c(bit a)
{
if(a==0)SDA=0; /*在此发出应答或非应答信号 */ else SDA=1;
_Nop();
_Nop();
_Nop();
SCL=1;
_Nop();
_Nop(); /*时钟低电平周期大于4μs*/
_Nop();
_Nop();
_Nop();
SCL=0; /*清时钟线,钳住I2C总线以便继续接收*/ _Nop();
_Nop(); }
32