智能车制作

 找回密码
 注册

扫一扫,访问微社区

查看: 17527|回复: 46
打印 上一主题 下一主题

龙丘智能科技贡献:MC9S12DG128 EEPROM完美版读写程序

  [复制链接]

80

主题

1045

帖子

0

精华

跨届大侠

北京龙邱智能科技有限公司

Rank: 10Rank: 10Rank: 10

积分
10332

论坛元老奖章

QQ
威望
2905
贡献
6151
兑换币
4515
注册时间
2008-6-7
在线时间
638 小时
跳转到指定楼层
1#
发表于 2009-11-20 20:35:42 | 只看该作者 回帖奖励 |正序浏览 |阅读模式
下面是一个完整的MC9S12DG128 EEPROM读写程序,程序来源于网络,反馈于网络,对大家肯定有用!下面仅贴出部分主文件和主函数,完整工程可以下载附件,开发板用户可以直接操作演示程序。
/********************************************************
龙丘MC9S12DG128 多功能开发板
Designed by Chiu Sir
E-mail:chiusir@163.com
软件版本:V1.2
最后更新:2009年8月25日
相关信息参考下列地址:
网站:  http://www.lqist.cn
论坛:  http://smartcar.5d6d.com
淘宝店:http://shop36265907.taobao.com
------------------------------------
Code Warrior 5.0
Target : MC9S12dg128b
Crystal: 16.000Mhz
busclock:16.000MHz
pllclock:32.000MHz
------------------------------------
【程序使用说明】:
【重要修改】
在Start12.c文件最前面加入:#define _HCS12_SERIALMON
在Project.prm中:
    添加下面的代码:
      EEPROM        = READ_ONLY     0x0800 TO   0x0FFB;
    修改下面的代码:
      RAM           = READ_WRITE    0x2000 TO   0x3FFF;
     
【串口波特率】:9600,n,8,1
【按UP】    修改欲写入的数据
【按OK】    对块的读操作
【按ESC】   对块的重新写入数据
【使用步骤】:
1.插上电源和串口线,并打开串口调试工具
2.按ESC读取原始数据
3.按UP/DOWN修改欲写入的数据
4.按OK写入新数据
5.复位单片机然后按ESC读取上次写入的数据,比对保存结果.  
*****************************************************************************************/
#i nclude <hidef.h>   
#i nclude "derivative.h"
#i nclude "LQprintp.h"
#i nclude "LQEEPROM.h"
volatile word testnumber=0;
/**************************************************
复杂测试操作:
1.按ESC读取原始数据
2.按UP/DOWN修改欲写入的数据
3.按OK写入新数据
4.复位单片机然后按ESC读取上次写入的数据,比对保存结果.
***************************************************/
void Test_EE_WriteRead(void)
{
    word cnt;
   
    if(PTIH_PTIH7==0)       //OK
    {
       printp("write and read data:\n");
       for(cnt=0;cnt<10;cnt+=2)         
       {         
          EEP_Wt_Word(EEPROM_ADDR_BASE+cnt,0xffff,EEP_Word_Erase);   
          EEP_Wt_Word(EEPROM_ADDR_BASE+cnt  ,0x1200+cnt+testnumber  ,EEP_Word_Prog);
          EEP_Wt_Word(EEPROM_ADDR_BASE+cnt+1,0x1200+cnt+testnumber+1,EEP_Word_Prog);
          u16ata=EEP_Rd_Word(EEPROM_ADDR_BASE+cnt);
          printp("%04x,",u16ata);
          u16ata=EEP_Rd_Word(EEPROM_ADDR_BASE+cnt+1);
          printp("%04x,",u16ata);         
       }
       printp("\n");
    }
    else if(PTIH_PTIH6==0)  //ESC
    {
       printp("read data:\n");
       for(cnt=0;cnt<10;cnt+=2)         
       {
         u16ata=EEP_Rd_Word(EEPROM_ADDR_BASE+cnt);
         printp("%04x,",u16ata);
         u16ata=EEP_Rd_Word(EEPROM_ADDR_BASE+cnt+1);
         printp("%04x,",u16ata);         
       }
       printp("\n");
    }
    else if(PTIH_PTIH2==0)  //UP
    {      
      printp("cnt:%04x \n",++testnumber);
    }
    else if(PTIH_PTIH5==0)  //DOWN
    {      
      printp("cnt:%04x \n",--testnumber);
    }
}
/**************************************************
总线时钟初始化函数:16MHz=晶振频率
编写:龙丘
最后修改:2009.11.18
***************************************************/
static void Set_Bus_16M(void)
{  
    CLKSEL=0X00;    //disengage PLL to system
    PLLCTL_PLLON=1;   //turn on PLL
    SYNR=1;         
    REFDV=1;          //pllclock=2*osc*(1+SYNR)/(1+REFDV)=32MHz;
    _asm(nop);          //BUS CLOCK=16M
    _asm(nop);
    while(!(CRGFLG_LOCK==1));   //when pll is steady ,then use it;
    CLKSEL_PLLSEL =1;          //engage PLL to system;
}
/**************************************************
main()
编写:龙丘
最后修改:2009.11.18
***************************************************/
void main(void)
{
  Set_Bus_16M();
  SCI_Init();
  printp("started!\n");
  DDRB =0xff;
  PORTB=0x00;
  EEPROM_init();   
  DisableInterrupts;
  PORTB=0xFF;
  for(;;)
  {
      //Simple_Test();
      Test_EE_WriteRead();
      Dly_ms(500);
      PORTB_BIT0=~ PORTB_BIT0;
  } /* wait forever */
  /* please make sure that you never leave this */
}

/**************************************************
延时函数:ms个毫秒
编写:龙丘
最后修改:2009.11.18
***************************************************/
void Dly_ms(int ms)  //x取值1~65536;
{
  int ii,jj;
   if (ms<1) ms=1;
   for(ii=0;ii<ms;ii++)
     for(jj=0;jj<1335;jj++);    //16MHz--1ms   
}
/**************************************************
延时函数:ms个毫秒
编写:龙丘
最后修改:2009.11.18
***************************************************/
void Dly_10us(int ms)  //x取值1~65536;
{
  int ii,jj;
   if (ms<1) ms=1;
   for(ii=0;ii<ms;ii++)
     for(jj=0;jj<13;jj++);    //16MHz--10us   
}
/*************************************************                 
EEPROM模块初始化程序  
编写:龙丘
最后修改:2009.11.18        
*************************************************/
void EEPROM_init(void)
{
    ECLKDIV=0x4A;                   // 采用8分频,因为OSCLK=16M,又为了使ECLK
                                    // 达到190KHZ,所以EDIV=21,即ECLK=16M/8/11=190.5KHZ
    ECNFG=0x00;                     // 禁止EEPROM相关操作的中断
    while(ECLKDIV_EDIVLD == 0);     // 等待时钟设置成功
    EPROT_EPOPEN=1;                 // EEPROM没有保护,可以进行编程与擦除
    EPROT_EPDIS=1;                  // EEPROM保护禁止
}
/*************************************************               
写EEPROM程序      
Addr:  要操作的EEPROM偏移地址
Data:  要操作的EEPROM数据
OpType:操作命令类型  
编写:龙丘                  
*************************************************/
void EEP_Wt_Word(word * Addr,word Data,byte OpType)
{
    DisableInterrupts;
    while(ESTAT_CBEIF == 0);        // 等待EEPROM命令缓冲区为空
    *Addr=Data;                     // 设置EEPROM的偏移地址
    ECMD=OpType;                    // 指令的类型
    ESTAT |= 0x80;                  // 清命令缓冲标志,开始执行命令
    while(ESTAT_CCIF == 0);         // 等待命令完成
    EnableInterrupts;
}
/*************************************************               
读EEPROM程序                 
Addr:  要操作的EEPROM偏移地址
返回值:读到的结果数据  
编写:龙丘
最后修改:2009.11.18               
*************************************************/
word EEP_Rd_Word(word * Addr)
{
    word TempData;
    DisableInterrupts;
    TempData=*Addr;                 // 读EEPROM的偏移地址的数据
    return(TempData);   
}
/*************************************************         
简单测试操作
编写:龙丘
最后修改:2009.11.18                  
*************************************************/
void Simple_Test(void)
{
   word cnt;   
   
   printp("write and read data:\n");
   for(cnt=0;cnt<1024;cnt+=2)         
   {         
      EEP_Wt_Word(EEPROM_ADDR_BASE+cnt,0xffff,EEP_Word_Erase);   
      EEP_Wt_Word(EEPROM_ADDR_BASE+cnt  ,0x1200+cnt,EEP_Word_Prog);
      EEP_Wt_Word(EEPROM_ADDR_BASE+cnt+1,0x1200+cnt+1,EEP_Word_Prog);
      u16ata=EEP_Rd_Word(EEPROM_ADDR_BASE+cnt);
      printp("%04x,",u16ata);
      u16ata=EEP_Rd_Word(EEPROM_ADDR_BASE+cnt+1);
      printp("%04x,",u16ata);      
      if(cnt%16==0) printp("\n");         
   } }
完整工程可以下载附件(密码:longqiu):

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x

1

主题

292

帖子

0

精华

跨届大侠

Rank: 10Rank: 10Rank: 10

积分
6297

优秀会员奖章活跃会员奖章论坛元老奖章

威望
4366
贡献
1319
兑换币
617
注册时间
2012-12-2
在线时间
306 小时
47#
发表于 2014-8-7 09:17:48 | 只看该作者
谢谢
回复 支持 反对

使用道具 举报

1

主题

18

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
310
威望
256
贡献
54
兑换币
5
注册时间
2011-6-3
在线时间
0 小时
46#
发表于 2014-7-26 11:17:00 | 只看该作者
谢谢!
回复 支持 反对

使用道具 举报

0

主题

100

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1217

活跃会员奖章优秀会员奖章

威望
590
贡献
349
兑换币
280
注册时间
2012-12-8
在线时间
139 小时
45#
发表于 2014-1-31 23:02:47 | 只看该作者
9楼和42楼碉堡了,一个pll,一个#pragma学习了
回复 支持 反对

使用道具 举报

0

主题

54

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1381
威望
727
贡献
416
兑换币
384
注册时间
2012-5-26
在线时间
119 小时
44#
发表于 2013-3-22 09:12:23 | 只看该作者
谢谢
回复 支持 反对

使用道具 举报

0

主题

7

帖子

0

精华

注册会员

Rank: 2

积分
146
威望
112
贡献
26
兑换币
0
注册时间
2012-5-17
在线时间
4 小时
毕业学校
苏州科技学院
43#
发表于 2012-5-17 16:11:01 | 只看该作者
42楼不错,了解了基本知识,遍地“黄金”啊
回复 支持 反对

使用道具 举报

19

主题

670

帖子

0

精华

常驻嘉宾

Rank: 8Rank: 8

积分
3466

热心会员奖章优秀会员奖章在线王奖章论坛元老奖章

威望
1709
贡献
977
兑换币
265
注册时间
2012-3-4
在线时间
391 小时
42#
发表于 2012-5-1 15:41:12 | 只看该作者


重要的#pragma声明
#pragma声明是基于单片机开发的特点而对标准C语法的一个扩充。它对充分利用单片机内各类有限的资源起到不可或缺的关键作用。下面简单介绍几个最常用的#pragma声明。

1 #pragma DATA_SEG
定义变量所处的数据段。其语法型式为:
#pragma DATA_SEG <属性> 名称
数据段名称可以自己任意命名,但习惯上有些约定的名称,其作用分别为:
 DEFAULT - 缺省的数据段,在08 系列单片机中的地址为0x100 以上。一般的变量定义可以放在这一区域。
 MY_ZEROPAGE - 特指第0页数据段,地址范围0x00-0xff,但实际用户可用的空间不到256 字节,因为前面的一些地址空间已经分配给了片内寄存器。需要频繁或快速存取的变量应该指定放在这一特殊区域,特别是位变量。
数据段名称必须和prm 文件中的数据段配置说明相关连才能真正发挥其定位作用。如果你自己命名的数据段在prm 文件中没有特别说明,那此数据段的性质等同于“DEFAULT”。
数据段的“属性”可以缺省,它主要的目的是告诉编译器此段数据可适用的寻址模式。不同的寻址模式所花的指令数量和运行时间都不同。对于08 系列单片机,关键的是第0页数据段可以用8位地址进行直接快速寻址,故对应此数据段应尽量指明其属性为“__SHORT_SEG”。对于一般数据段没有属性描述,其缺省是
“__FAR_SEG”,将用16位地址间接寻址。
举几个数据段定义的例子加以进一步说明。
#pragma DATA_SEG __SHORT_SEG MY_ZEROPAGE //开始0 页数据定义
volatile struct {
unsigned powerOn : 1;
unsigned alarmOn : 1;
unsigned commActive : 1;
unsigned sysError : 1;
} myFlag;
volatile word msCounter;
byte i,j,k;
#pragma DATA_SEG DEFAULT //开始普通数据段定义(结束0 页数据段)byte tmpBuff[16];

2 #pragma CONST_SEG
定义一个常数数据段,必须和变量的const修饰关键词配合使用。其语法型式为:
#pragma CONST_SEG 名称
该数据段下定义的所有数据将被放置在程序只读的ROM 区,也就是08 系列单片机内的Flash 程序空间区。常数段名称可以用户自由定义,但一般都用“DEFAULT”,让连接器按可用的ROM区域自由分配变量位置。举例如下:
#pragma CONST_SEG DEFAULT
const byte prjName[]=”This is a demo”;
FSL 系列单片机开发及C语言编程简介
const word version = 0x0301;
#pragma CONST_SEG DEFAULT
word version = 0x0301; //没有const 该变量将被放置在RAM 区!
#pragma DATA_SEG DEFAULT
const word version = 0x0301; //尽管有const 但该变量将被放置在RAM 区!
3.3.3 #pragma INTO_ROM
功能类似于“CONST_SEG”,和变量修饰词“const”配合使用。但它只定义一个常数变量到ROM区,且只作用于紧接着的下一行定义。例如:
#pragma INTO_ROM
const byte prjName[]=”This is a demo”; //变量将被放置在ROM 区
word verData = 0x0301; //变量将被放置在缺省RAM 区

4 #pragma CODE_SEG
用以定义程序段并赋以特定的段名,语法型式如下:
#pragma CODE_SEG <属性> 名称
一般的程序设计是无需对代码段做特殊处理的。因为所有传统的08 系列单片机其程序空间都不超过64KB(16 位寻址最大范围)且在内存地址中呈线性连续分布。对于项目中所有的代码文件或库文件,连接器会在最后按程序模块出现的先后顺序挨个自动安排所有程序函数在内存中所处的实际位置,用户不必太关心某一个函数的具体位置。但最新推出的几款8 位机程序将超过64KB,这样必须在内存空间中以页面型式映射到首64KB地址范围,其对应的程序段属性要特殊声明。某些特殊的设计需要将不同部分的程序分别定位到不同的地址空间,例如实现程序代码下载自动更新。这样的设计需要把负责应用程序下载更新的驱动代码固定放置在一个保留区域内,而把一般的应用程序放置在另外一个区域以便在需要时整体擦除后更新。这时就需要用“CODE_SEG”来分别指明不同的程序段,但还必须配合prm文件对程序空间进行分配和指派。
代码段的属性一般都用缺省的“__FAR_SEG”,表明所有的函数调用都是长调用(对应汇编指令为JSR)。但C08 和S08 系列单片机支持效率更高的函数短调用(对应汇编指令为BSR),如果你的某一个功能模块含有多个相互调用的小函数且函数调用间距不超过+127 或-128 字节,则可以将这部分代码段声明为短调用属性“__NEAR_SEG”。但实际编程时由于C 代码对应的汇编指令长度不是很容易就能估测得到,所以短调用属性很少使用。
下面以几个实例进一步说明:
//定义缺省的代码段,缺省属性为远调用
#pragma CODE_SEG DEFAULT
void main(void)
FSL 08系列单片机开发及C语言编程简介
{
...
}
//定义名字为FUNC_CODE 的代码段,缺省属性为远调用
#pragma CODE_SEG FUNC_CODE
void MyApp(void)
{
}
//定义远调用的程序段,段名为BOOTLOAD
#pragma CODE_SEG __FAR_SEG BOOTLOAD
void BootLoader(void)
{
}
//定义近调用的程序段,段名为KEYBOARD
#pragma CODE_SEG __NEAR_SEG KEYBOARD
void KeyDebounce(void)
{
...
}
byte KeyCheck(void)
{
...
}
void KeyBoard(void)
{
if (keyCheck()) {
KeyDebounce();
...
}
}

5 #pragma TRAP_PROC
用于定义一个函数为中断服务类型。此类型的函数编译器在将C 代码编译成汇编指令时会在代码前后增加必要的现场保护和恢复汇编代码,同时函数的最后返回用汇编指令“RTI”而不是针对普通函数的“RTS”。例如:
#pragma TRAP_PROC
void SCI1_Int(void) { //定义SCI1的中断服务程序
...
}
注意用“TRAP_PROC”定义的中断服务函数其实际中断矢量地址必须通过prm 文件指派。

6 #pragma MESSAGE
这个声明用以控制编译信息的显示。一般情况下这些编译信息都是有用的,特别是告警和错误信息。但有时我们会按单片机的工作特性编写一些代码,但正常程序编写时这些代码会产生一些告警信息,例如
#pragma MESSAGE DISABLE C4002 //忽略“Result-not-used”告警
//==============================================================
// sci1 1 data receive interrupt service routine
// Assigned for full-duplex communication with Main board
//==============================================================
void interrupt 17 sci11_Receive_ISR(void)
FSL 08系列单片机开发及C语言编程简介
{
SCI1S1; //读一次状态寄存器清除中断标志,会产生C4002 告警
sci1RxFifo[sci1FifoPut] = SCI1D;
sci1FifoPut++;
sci1FifoPut &= (SCI1_RXFIFO_SIZE-1);
}
如果你不想每次都看见编译器给出的这一类信息,可以先确认这一信息的编号,然后用“#pragma MESSAGE”加上“DISABLE” 关键词和信息号将它屏蔽。如果你想特别关注某类信息,可以用“ENABLE”让其永远显示出来。

BMD下载器, MC9S12XS128, 北京龙丘智能科技, 核心板, 智能车
回复 支持 反对

使用道具 举报

15

主题

315

帖子

0

精华

常驻嘉宾

Rank: 8Rank: 8

积分
5440

热心会员奖章优秀会员奖章活跃会员奖章论坛元老奖章资源大师奖章在线王奖章

威望
1409
贡献
3421
兑换币
109
注册时间
2011-10-9
在线时间
305 小时
41#
发表于 2011-11-29 22:15:18 | 只看该作者
不错不错。。。
回复 支持 反对

使用道具 举报

2

主题

105

帖子

0

精华

金牌会员

CROSS

Rank: 6Rank: 6

积分
2059
威望
840
贡献
959
兑换币
160
注册时间
2010-12-20
在线时间
130 小时
40#
发表于 2011-9-23 11:28:40 | 只看该作者
thanks
回复 支持 反对

使用道具 举报

4

主题

81

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1227
威望
709
贡献
294
兑换币
152
注册时间
2010-11-16
在线时间
112 小时
39#
发表于 2011-8-16 16:44:47 | 只看该作者
非常感谢啊
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

关于我们|联系我们|小黑屋|智能车制作 ( 黑ICP备2022002344号

GMT+8, 2024-12-29 01:30 , Processed in 0.137961 second(s), 33 queries , Gzip On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表