Board logo

标题: 基于ARM单片机的单总线应用 [打印本页]

作者: luoman    时间: 2010-8-18 09:09     标题: 基于ARM单片机的单总线应用

基于ARM单片机的单总线应用

单总线(1-Wire)是美国达拉斯半导体公司的一项专利技术。与目前广泛应用的其他串行数据通信方式不同,它采用单根信号线完成数据的双向传输,具有节省I/O引脚资源、结构简单、成本低廉、便于总线扩展等诸多优点。关于单总线技术及其芯片的介绍,请参考有关资料。
⒈      硬件设计
在我的博文“基于51单片机的单总线”中介绍了采用51单片机、DS18B20和1602LCM等组成的温度测量显示装置,为了进行比较,现将单片机换成ARM而其它部分不变,即用DS18B20测量温度,通过1602LCM显示温度值,其原理图如下所示。另外本人使用的是Easy ARM1138开发板,这样安装调试都很方便。


⒉        软件设计
本设计要求用DS18B20测量温度,通过1602LCM显示温度值,其流程图如下所示。需要注意的是,在启动温度转换后需要等待750毫秒以上,本例是采用延时的方式等待转换结束,如果在此期间CPU还要做其它事,则可以采用定时中断的方式来等待转换结束。


该设计详细C语言程序如下,可以把51单片机的程序与之相比较,使自己更快的熟悉ARM单片计。

/***************************************************
基于ARM单片机  DS18B20+1602LCM
PF0-DQ(18B20),以下LCM:PA-数据,PF5-RS,PF6-RW,PF7-E
****************************************************/
#include  "systemInit.h"
#include  <hw_types.h>
#include  <hw_memmap.h>
#include  <hw_sysctl.h>
#include  <hw_gpio.h>
#include  <sysctl.h>
#include  <gpio.h>

//定义18B20
#define  DQ                    GPIO_PORTF_BASE , GPIO_PIN_0

//定义LCD
#define RS  GPIO_PORTF_BASE,GPIO_PIN_5
#define RW  GPIO_PORTF_BASE,GPIO_PIN_6
#define E   GPIO_PORTF_BASE,GPIO_PIN_7
#define AIO   GPIO_PORTA_BASE,0xff

#define uchar unsigned char
#define uint unsigned int

uchar display[5]={0,0,0,0,0};
uchar ditab[16]={0,1,1,2,3,3,4,4,5,6,6,7,8,8,9,9};    //温度的小数值
uchar tplsb,tpmsb;

//  延时1us
void  Delay(unsigned int  ulVal)
{
  unsigned int i,j;
  for(i=0;i<ulVal;i++)
    j++;  
}

//  延时1ms
void  Delay1(unsigned int  ulVal)
{
  unsigned int i,j;
  for(i=0;i<ulVal;i++)
  {
    for(j=0;j<1000;j++)
    Delay(1);
  }
}

//  定义全局的系统时钟变量
unsigned long TheSysClock = 12000000UL;

//  系统时钟初始化
void clockInit(void)
{
    SysCtlLDOSet(SYSCTL_LDO_2_50V);                   //  设置LDO输出电压

    SysCtlClockSet(SYSCTL_USE_OSC |                  //  系统时钟设置
                   SYSCTL_OSC_MAIN |                //  采用主振荡器
                   SYSCTL_XTAL_6MHZ |               //  外接6MHz晶振
                   SYSCTL_SYSDIV_1);                //  不分频

    TheSysClock = SysCtlClockGet();              //  获取当前的系统时钟频率
}

//LCD初始化
void LCD_Init(void)
{
  int i;
  GPIOPinWrite(AIO,0x30);  //GPIOA口出0x30
  GPIOPinWrite(RS,0) ;  //RS=0
  GPIOPinWrite(RW,0) ;  //RW=0
  for(i=0;i<3;i++)
  {
    GPIOPinWrite(E,0x01<<7) ;  //E=1
    GPIOPinWrite(E,0) ;  //E=0
    Delay(500* (TheSysClock / 4000000));      //  延时约500us      
  }
}

//LCD命令写
void Write_Com(uchar a)
{
  Delay(1000* (TheSysClock / 4000000));      //  延时约1ms
  GPIOPinWrite(RW,0);  //RW=0
  GPIOPinWrite(RS,0) ;  //RS=0
  GPIOPinTypeOut(AIO);    //GPIO A为输出
  GPIOPinWrite(AIO,a);  //GPIO A口出a
  GPIOPinWrite(E,0x01<<7) ;  //E=1
  GPIOPinWrite(E,0) ;  //E=0
}

//LCD数据写
void Write_Data(uchar a)
{
  Delay(1000* (TheSysClock / 4000000));      //  延时约1ms
  GPIOPinWrite(RW,0) ;  //RW=0
  GPIOPinTypeOut(AIO);    //GPIOA为输出
  GPIOPinWrite(RS,0x01<<5) ;  //RS=1
  GPIOPinWrite(AIO,a);  //GPIO A口出a
  GPIOPinWrite(E,0x01<<7) ;  //E=1
  GPIOPinWrite(E,0) ;  //E=0
}

/* 产生复位脉冲初始化DS18B20 */
void TxReset(void)
{
  uint i;
  HWREGB(0x40025400)|=1;          //设置DQ为输出
  GPIOPinWrite(DQ, 0);          /* 拉低约900us */
  i= 1000;
  while (i>0)   i--;   
  GPIOPinWrite(DQ, 1);              // 产生上升沿
  i = 36;
  while (i>0)   i--;
}

/* 等待应答脉冲 */
void RxWait(void)
{
  uint i,data;
  HWREGB(0x40025400)&=0xfe;            //设置DQ为输入
  do
  {
    data=GPIOPinRead(DQ);
    data=(data)&1;
  }
  while(data);
  do
  {
    data=GPIOPinRead(DQ);
    data=(data)&1;
  }
  while(!data);        // 检测到应答脉冲
  i = 15;
  while (i>0)   i--;
}

/* 读取数据的一位,满足读时隙要求 */
uchar RdBit(void)
{
  uint i,b;
  HWREGB(0x40025400)|=1;     //设置DQ为输出
  GPIOPinWrite(DQ, 0);
  i =1;
  while (i>0)   i--;
  HWREGB(0x40025400)&=0xfe;          //设置DQ为输入
  GPIOPinWrite(DQ, 1);
  i = 15;
  while (i>0)   i--;
  b = GPIOPinRead(DQ);
  b=(b)&1;
  i = 110;
  while(i>0) i--;
  return (b);
}

/* 读取数据的一个字节 */
uchar RdByte(void)
{
  uchar i,j,b;
  b = 0;
  for (i=1;i<=8;i++)
  {
    j = RdBit();
    b = (j<<7)|(b>>1);
  }
  return(b);
}

/* 写数据的一个字节,满足写1和写0的时隙要求 */
void WrByte(uchar b)
{
  uint i;
  uchar j,btmp;
  HWREGB(0x40025400)=1;       //设置DQ为输出
  for(j=1;j<=8;j++)
  {
    btmp = b&1;
    b = b>>1;       // 取下一位(由低位向高位)
    if (btmp)
    {       /* 写1 */
      GPIOPinWrite(DQ, 0);
      i = 15;
      while(i>0) i--;   // 延时,使得15us以内拉高
      GPIOPinWrite(DQ, 1);
      i = 100;
      while(i>0) i--;   // 整个写1时隙不低于60us
    }
    else
    {       /* 写0 */
      GPIOPinWrite(DQ, 0);         
      i = 110;
      while(i>0) i--;   // 保持低在60us到120us之间
      GPIOPinWrite(DQ, 1);
      i = 15;
      while(i>0) i--;
     }
  }
}

/* 启动温度转换 */
void convert(void)
{
  TxReset();            // 产生复位脉冲,初始化DS18B20
  RxWait();         // 等待DS18B20给出应答脉冲
  Delay1(1);            // 延时
  WrByte(0xcc);     //跳过rom命令
  WrByte(0x44);     //启动温度转换命令
}

/* 读取转换结果 */
void RdTemp(void)
{
  TxReset();            // 产生复位脉冲,初始化DS18B20
  RxWait();         // 等待DS18B20给出应答脉冲
  Delay1(1);            // 延时
  WrByte(0xcc);     //跳过rom命令
  WrByte(0xbe);     //读取暂存器命令
  tplsb = RdByte(); // 温度值低位字节(其中低4位为二进制的“小数”部分)
  tpmsb = RdByte(); // 高位值高位字节(其中高5位为符号位)     
}

//温度数据处理函数
void work_temp(void)
{
  unsigned char n=0;
  if(tpmsb>127)
  {
    tpmsb=(256-tpmsb);        //负温度求补码
    tplsb=(256-tplsb);
    n=1;
  }
  display[4]=tplsb&0x0f;
  display[0]=ditab[display[4]];                     //温度的小数
  display[4]=((tplsb&0xf0)>>4)|((tpmsb&0x0f)<<4);
  display[3]=display[4]/100;                        //温度的百位
  display[1]=display[4]%100;
  display[2]=display[1]/10;                         //温度的十位
  display[1]=display[1]%10;                         //温度的个位
  if(n)
  Delay(1);             //延时10us
}   

//  主函数(程序入口)
int main(void)
{
  clockInit();                               //  时钟初始化:晶振,6MHz
  SysCtlPeriEnable(SYSCTL_PERIPH_GPIOA);    //使能GPIOA口外设
  GPIOPinTypeOut(GPIO_PORTA_BASE ,0xFF);    //GPIOA为输出
  SysCtlPeriEnable(SYSCTL_PERIPH_GPIOF);    //使能18B20DAT所在的GPIO端口
  SysCtlPeriEnable(SYSCTL_PERIPH_GPIOF);    //使能GPIOF口外设
  GPIOPinTypeOut(GPIO_PORTF_BASE ,0xFF);    //GPIOF为输出

  LCD_Init();                          //LCD初始化
  Write_Com(0x38);                    //设置工作方式
  Write_Com(0x01);                    //清除显示
  Write_Com(0x06);                    //设置输入方式
  Write_Com(0x0c);                    //设置显示方式
  Write_Com(0x80);                    //DDRAM第一行的首地址
  Write_Data(0x54);                  //显示Test by DS18B20
  Write_Data(0x65);
  Write_Data(0x73);
  Write_Data(0x74);
  Write_Data(0x20);
  Write_Data(0x62);
  Write_Data(0x79);
  Write_Data(0x20);
  Write_Data(0x44);              
  Write_Data(0x53);
  Write_Data(0x31);
  Write_Data(0x38);
  Write_Data(0x42);
  Write_Data(0x32);
  Write_Data(0x30);
  for (;;)
  {
    convert();               //启动温度转换
    Delay1(800);             //延时800ms
    RdTemp();              //读取转换结果
    work_temp();           //温度数据处理
    GPIOPinTypeOut(GPIO_PORTF_BASE ,0xFF);    //GPIO F为输出
    Write_Com(0xc0);                    //DDRAM第二行的首地址
    Write_Data(0x54);                   //显示Temp:
    Write_Data(0x65);
    Write_Data(0x6d);
    Write_Data(0x70);
    Write_Data(0x3a);
    if(display[3]==0)                   //显示温度值
          Write_Data(0x20);
    else
        Write_Data(0x30+display[3]);
    Write_Data(0x30+display[2]);
    Write_Data(0x30+display[1]);
    Write_Data(0x2e);
    Write_Data(0x30+display[0]);
    Write_Data(0xdf);
    Write_Data(0x43);
  }
}

本人使用的是IAR EWARM(IAR Embedded Workbench for ARM)集成开发环境,利用周立功公司提供的工程模板,输入以上的源程序,点击菜单“Project”→“Mark”进行编译,或按F7键,根据提示修改错误,再进行编译直至无错误为止 。

3  软件调试
编译无误后即可点击菜单“Project”→“Dbuge”下载源程序的机器码至开发板(或按Ctrl+D),同时在桌面上出现了几个调试用的快捷按钮——运行(go)、运行到光标处(Run to Cursor)、步出(Step Out)、步入(Step Into)、步越(Step Over)、停止(Break)、复位(Reset)等。通过选择上述不同的快捷按钮,以及观察变量的值(点击菜单“View”→“Watch”在Expression中输入变量名,即可在Value中看到该变量的值;或将光标悬停在某变量上,即可看到该变量的值),来调试程序,直至一切无误后即可全速运行了(点击快捷按钮go)。实物照片如下所示。




欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) Powered by Discuz! 7.0.0