水乐天 发表于 2012-9-11 17:16:37

[深入浅出Energia开发第一讲]51程序的移植

本帖最后由 水乐天 于 2012-9-12 17:50 编辑

最近听弘毅讲LaunchPad搞活动促销,超便宜,于是俺买了10块...慢慢弄总会用到滴{:soso_e154:},这几天反复折磨有一点小小心得,不敢独享,于是打算写出来与大伙分享。

下面放出是淘宝网店地址,注意如果数量比较多的话需要有团购代码的,需要的童鞋可以去购买:
http://item.taobao.com/item.htm?id=16603403744
----------------------------------------------------------------------------------------------------------------------------
首先声明下:本文不针对零基础的朋友,如果对一般开发常识、C语言、C++完全没有了解,请绕行。

准备工作一:
俗话说工欲善其事必先利其器,首先我们来谈谈编译环境的选择:
官方的编译环境如果没有使用过请参考《Energia介绍--MSP430的Arduino IDE》或直接下载

但是官方的编译器有各种问题。。。比如中文注释不能书写。。。于是我仅用官方的编译器做调试用途,平时书写代码....还是算了吧。
我建议Windows用户选择UtralEdit IOS及Linux用户选择Sublime text 2
另外为了让我们的代码看起来更舒服 附赠代码格式化工具
----------------------------------------------------------------------------------------------------------------------------
准备工作二:
之后我们要对手头的LaunchPad做些小小的处理:

第一步 将RXD与TXD 两个跳线横过来,因为串口输出到PC需要这两个端口,否则无法进行串口输出。
第二步 将P1.6和P1.0两个LED等的跳线拔掉,因为这两个端口跟I2C端口冲突,所以在调试的时候如果你不需要这两个LED最好将其拔掉。
第三步 注意如果你的程序要使用串口的话,那么3号和4号引脚尽量不要使用,否则会影响串口数据的输出。
为了让大家更清楚地了解,如下图所示:

----------------------------------------------------------------------------------------------------------------------------
1.1 首先让我们来看一个51的程序。
   本人本行是开发上位系统软件基本是硬件盲,在写这个之前完全没接触过51单片机,也没写过51的程序,各位硬件高手不要见笑哈。但是我觉得从太简单的程序入手没意思哈,于是从公司同事手中借来个台湾燃太的TN901红外温度传感器和51的范例程序。于是我们就拿这个程序开始我们的改造好了。如下图所示:

   因为51的完整代码比较长还包括LCD的显示,所以我们摘主要的来看,想看完整代码点下载   
   主要我们需要转换的部分的包括这几个部分
   一、端口定义:
sbit TN_Data = P1^0; //定义TN9接口
sbit TN_Clk= P1^2;
sbit TN_ACK= P1^4;

   二、主程序部分:

/*----------------------------------主程序入口-----------------------------------*/
void main()
{
    LCD_Init();      //LCD初始化
    Delay5Ms();
    LCD_Write_String(0, 0, Range);
    LCD_Write_String(0, 1, table);
    Delay400Ms();
    TN_ACK = 1;
    while(1)
    {

      TN_ACK = 0;
      TN_ReadData(0x4c);                //目标温度的第一个字节为0x4c

      if((ReadData == 0x4c) && (ReadData == 0x0d))   //每帧的最后一个字节为0x0d
      {
            TN_GetData_Target();
      }

      DelayMs(1);

      TN_ACK = 0;
      TN_ReadData(0x66);                //环境温度的第一个字节为0x66

      if((ReadData == 0x66) && (ReadData == 0x0d)) //每帧的最后一个字节为0x0d
      {
            TN_GetData_Temp();
      }
    }
}


   三、IIC接口数据传输:
void TN_ReadData(uchar flag)        //读数据
{
    uchar i,j,k;
        bit BitState = 0;                                        //每次发七帧
        for(k=0;k<7;k++)
        {
                  for(j=0;j<5;j++)                           //每帧5个字节
               {
                  for(i=0;i<8;i++)
                        {
                           while(TN_Clk);
                           BitState= TN_Data;
                           ReadData= ReadData<<1;
                           ReadData= ReadData|BitState;
                           while(!TN_Clk);

                        }
               }

               if(ReadData==flag)k=8;
        }

        TN_ACK=1;
}
   虽然我从没写过51的程序,但是我觉得大体上51程序结构都应该差不多,所以我按照这种方法来转换就好,这个程序搞定了基本其它程序也类似如此。
   首先我们来转换,第一部分端口的定义,51这里用的类型是sbit,对应应该是byte 0或1 估计是标识每个端口的高低电平,但是我们这里与51程序的含义不同应该标识具体的端口号,所以我们这里采用int类型,如下所示:
       intTN_Data=7;
    int TN_Clk=8;
    int TN_ACK=9;
   之后我们来看程序的入口函数,注意这里的端口值改变比如 TN_ACK=1; (51代码原来很精简哦)其实是给ACK端口赋值为高电平,所以我们这里应该对应的写法是pinMode(TN_ACK, OUTPUT);//指定ACK引脚为输出脚
digitalWrite(TN_ACK,HIGH);//将指定端口输出为高电平
   为了处理方便我们将51主方法中的代码分割在Setup和Loop方法中执行于是,我们将其分割为两个部分,将初始化部分放在Setup中执行,While(1)中的部分放在Loop中执行,如下所示:
//初始化部分
void setup()
{
    pinMode(TN_Clk, INPUT);   //设定时钟口为输入端口
    pinMode(TN_ACK, OUTPUT);//设定ACK口为输出端口
    digitalWrite(TN_ACK,HIGH);//在ACK口输出高电平
}

//采集部分
void loop()
{
   digitalWrite(TN_ACK,LOW);
   TN_ReadData(0x4c);                        //目标温度的第一个字节为0x4c   
   if((ReadData==0x4c)&&(ReadData==0x0d))//每帧的最后一个字节为0x0d   
   {   
      Target=TN_GetData_Target();   
   }
                  
   delay(1);
         
   digitalWrite(TN_ACK,LOW);
   TN_ReadData(0x66);                        //环境温度的第一个字节为0x66      

   if((ReadData==0x66)&&(ReadData==0x0d))//每帧的最后一个字节为0x0d   
   {   
       Temp=TN_GetData_Temp();
   }
}

然后我们来看IIC数据传输的部分,大体逻辑我是这样理解的,时钟作为一个标志位,每当时钟高低电平改变的时候采用将1字节的数据存储起来并左移一位,直到出现结束标志位为止,这里我们要注意下51程序中while(TN_Clk);这样的地方。在这里我们TN_Clk代表的不是具体端口的高低电平所以要换成这样的写法while(val)
                {
                  val = digitalRead(TN_Clk);//获取端口的高低电平
                }
之后我们把这部分的代码转换过来,如下所示:
void TN_ReadData(char flag)        //读数据
{
    char i, j, k;
    byte BitState = 0;                                        //每次发七帧
    for(k = 0; k < 7; k++)
    {
      for(j = 0; j < 5; j++)                       //每帧5个字节
      {
            for(i = 0; i < 8; i++)
            {
                int val = digitalRead(TN_Clk);//注意这里51是直接读端口号所以我们要进行转换
                while(val)
                {
                  val = digitalRead(TN_Clk);
                }
                BitState = digitalRead(TN_Data);
                ReadData = ReadData << 1;
                ReadData = ReadData | BitState;
                while(!val)
                {
                  val = digitalRead(TN_Clk);
                }
            }
      }

      if(ReadData == flag)k = 8;
    }

    digitalWrite(TN_ACK, HIGH);
}

再给这个程序加上串口输出的部分,我们这次的内容就大功告成了:)
int TN_Data = 15;
int TN_Clk = 14;
int TN_ACK = 13;
unsigned charReadData;
short Temp,Target;

void setup()
{
    Serial.begin(9600);
    pinMode(TN_Clk, INPUT);//
    pinMode(TN_ACK, OUTPUT);
    digitalWrite(TN_ACK,HIGH);
}

void loop()
{
   digitalWrite(TN_ACK,LOW);
   TN_ReadData(0x4c);                //目标温度的第一个字节为0x4c   
   if((ReadData==0x4c)&&(ReadData==0x0d))         //每帧的最后一个字节为0x0d   
   {   
      Target=TN_GetData_Target();   
   }
                  
   delay(1);
         
   digitalWrite(TN_ACK,LOW);
   TN_ReadData(0x66);                //环境温度的第一个字节为0x66      

   if((ReadData==0x66)&&(ReadData==0x0d))    //每帧的最后一个字节为0x0d   
   {   
       Temp=TN_GetData_Temp();
   }
}

void SerialValue()
{
   Serial.print("Target: ");
   Serial.print(Target, DEC);
   Serial.println(" C");
   Serial.print("Temp: ");
   Serial.print(Temp, DEC);
   Serial.println(" C");
}

void TN_ReadData(char flag)        //读数据
{
      char i,j,k;
        byte BitState = 0;                                        //每次发七帧
        for(k=0;k<7;k++)
        {
                  for(j=0;j<5;j++)                           //每帧5个字节
               {
                  for(i=0;i<8;i++)
                        {
                           int val= digitalRead(TN_Clk);
                           while(val)
                           {
                               val = digitalRead(TN_Clk);
                           }
                           //val = digitalRead(TN_Clk);
                           BitState= digitalRead(TN_Data);
                           ReadData= ReadData<<1;
                           ReadData= ReadData|BitState;
                          while(!val)
                        {
                              val = digitalRead(TN_Clk);
                        }
                        }
               }

               if(ReadData==flag)k=8;
        }

        digitalWrite(TN_ACK,HIGH);
}

int TN_GetData_Temp()   
{   
    int Temp;   
    Temp=(ReadData<<8)|ReadData;   
    Temp = (float)Temp/16 - 273.15;   
    Temp=Temp*100;                               //温度值乘100,以方便计算小数点后两位   
   
    return Temp;
}   

int TN_GetData_Target()   
{   
    int Target;   
    Target=(ReadData<<8)|ReadData;   
    Target = (float)Target/16 - 273.15;   
    Target=Target*100;                               //温度值乘100,以方便计算小数点后两位   
   
    return Target;
}


最后我们来总结下51到Arduino程序转换需要注意的地方:

第一:端口定义的转换,因为端口取值的方式不同所以写法上要转换下,个人觉得从设计模式的角度上讲,51的端口取值方式虽然很方便,但是容易造成变量定义时的混淆,有时候多写几行代码未必是坏事。

第二:因为Arduino的基本结构包含Setup和Loop,51这里一般用一个死循环直接实现。所以这里要注意下程序哦分割。

第三:注意效率问题,比如Serial输出这样的方法消耗一定的时间,一次可能影响很小,但是如果套在多层循环中和需要速度的处理中影响会很大。有兴趣的朋友可以试试把串口输出的实际时间消耗。最后把程序模块化分割,冗余的逻辑。

其他方面我觉得程序移植起来还是很方便的,基本大部分51的代码都可以用Arduino来实现。本次因为这是测试代码所以有些小乱,我将会在后面的文章中详细讲解下如何整理和封装代码的步骤,让大家写起程序更加模块化:)

nust_奔跑 发表于 2012-9-11 17:28:31

买10块干吗用~?

darkorigin 发表于 2012-9-11 20:03:59

性能到底如何?好像是32K的处理器?好像到处都没说详细硬件性能啥的;
还是ARDUINO描述全面

darkorigin 发表于 2012-9-11 20:10:25

外部晶震32K的,好像是16K的混合信号微控制器。。。具体能做什么还是不太懂。。。哎~~~~
看百度介绍控制器的说明好像主要还是低成本的数据采集处理

tornado919 发表于 2012-9-11 20:13:47

:)不错啊,我好想买的

darkorigin 发表于 2012-9-11 23:20:24

不知道51的代码移植的效果如何,其实51应该是单片机比较入门的普及型机,很多大学的计算机系MSC51和Z80都是必修的专业课。。。
都能移植应该很强大了,而且这个东东貌似比51更强大?

水乐天 发表于 2012-9-11 23:31:19

本帖最后由 水乐天 于 2012-9-11 23:32 编辑

是的哈51的代码应该理论上都可以移植,这个东西普及了基本51市场就很小了:)我觉得用Arduino开发的最大好处是可以让新手和专家在开发时专注业务逻辑。

┏ωǒ┛菰独 发表于 2012-9-12 08:57:20

darkorigin 发表于 2012-9-11 20:03 static/image/common/back.gif
性能到底如何?好像是32K的处理器?好像到处都没说详细硬件性能啥的;
还是ARDUINO描述全面

16M内部振荡器

┏ωǒ┛菰独 发表于 2012-9-12 08:57:39

darkorigin 发表于 2012-9-11 20:10 static/image/common/back.gif
外部晶震32K的,好像是16K的混合信号微控制器。。。具体能做什么还是不太懂。。。哎~~~~
看百度介绍控制器 ...

16M........不是K......

┏ωǒ┛菰独 发表于 2012-9-12 08:57:58

darkorigin 发表于 2012-9-11 23:20 static/image/common/back.gif
不知道51的代码移植的效果如何,其实51应该是单片机比较入门的普及型机,很多大学的计算机系MSC51和Z80都是 ...

MCS51弱爆了

JAY 发表于 2012-9-12 18:07:35

这个要顶~写得太详细了,对于初学launchpad,用Arduino开发的人来说太有用了!图文并茂,工具下载链接,程序代码及注释,注意事项,神马都有了,哈哈~~   向LZ学习!

darkorigin 发表于 2012-9-12 20:15:27

┏ωǒ┛菰独 发表于 2012-9-12 08:57 static/image/common/back.gif
MCS51弱爆了

确实MSC51很弱。但是51系列单片机貌似是出货量和存世量都很大的控制器。
代码神马的也都很经典啊。包括89c52啥的 也都是大量被应用的。
弱主要在性能上,但是强在便宜,毕竟周边元件也少,做一些简单应用完全够了,比如计算器,比如定时装置,比如其他设备的周边(比如显示模块的控制电路)也够了。

这个主控能移植51代码那么代码量上和例程上基本上真的是很广阔啊。。。哇咔咔

性能到底如何呢?16M的内部震荡,好像外部震荡频率还支持更高的?

darkorigin 发表于 2012-9-12 20:16:02

┏ωǒ┛菰独 发表于 2012-9-12 08:57 static/image/common/back.gif
16M........不是K......

额。呵呵 手误。哈哈~~~SORRY:lol

darkorigin 发表于 2012-9-12 20:16:58

┏ωǒ┛菰独 发表于 2012-9-12 08:57 static/image/common/back.gif
MCS51弱爆了

Z80好像还要弱。 现在好像没有看到神马产品了

darkorigin 发表于 2012-9-12 20:24:20

水乐天 发表于 2012-9-11 23:31 static/image/common/back.gif
是的哈51的代码应该理论上都可以移植,这个东西普及了基本51市场就很小了:)我觉得用Arduino开发的最大好处 ...

是的,其实这也是程序设计的潮流,逐渐的通过强化IDE界面简化编程难度,使得程序员更多精力不会被牵制在那些冗余简单的代码上(比如C++ 和VC++ 到C# )不用自己画界面而采用组件的方式编程(当年TC2.0的时候UCDOS带的几个画图的例程和库文件我学了几个礼拜)
更多的让程序员去强化功能和逻辑(算法)上。
记得当年学MSC51时候 神马 基址变址寄存器 神马的搞死人了 用了C去做单片编程就已经是个里程碑(至少省略了初始化的几个堆栈操作)
页: [1] 2
查看完整版本: [深入浅出Energia开发第一讲]51程序的移植