打印
[应用方案]

基于新唐N76E003的三轴加速度485远传系统(modbusRTU)

[复制链接]
6084|45
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
本帖最后由 Harvard 于 2017-7-4 22:35 编辑

基于新唐N76E003的三轴加速度485远传系统(modbusRTU)

   关注新唐的003有段时间了,简洁,功能强大 ,引脚少. 加上1.x的价格 还是适合做很多很多的应用.51内核.比起M0, 可以省去大量的库函数. 官方的BSP 与M051的bsp一比, 有点安然失色.好在是8位机, 自己稍加修改所带的例程,基本可以把芯片玩转. 怎么说? 51 还是大家都熟悉的. 尤其是我们这帮70 after,和80after. 51相对比较熟悉.
   用的过程中,也是遇到一些问题,基本都在论坛解决了.
其一:  
    在mdk中,通过nulink烧写,无法进行config加密, 只能到icp 软件中进行. 在icp中.选择8051 1T单片机即可; 不知道为何在使用003时所用的固件不是最新的而是比最高版本有一个版本号的差距.导致正在使用的M0调试器,与51调试器 需要来回的切换固件版本.

其二:
   在MDK5.23中使用时,使用nulink 插件时, debug,无法在c中,单步运行或者设置断点,光标一直在汇编的disassebly窗口中. 而这一功能在mdk4中可以正常运行. 官方FAE建议重装MDK4. MDK5 懒得折腾了.就用mdk4.74进行开发吧;


其三:
     在mdk中调试的时候,虽然勾选了periodical update,但是watch窗口中的数据无法在全速运行中进行查看 带来诸多不变, 要查看数据,只有进行暂停, 这点没有在调试M051方便, 是因为8051的内置调试单元与M051中的内置调试单元导致的么? 求解.

     目前,遇到的问题就是这么几个; 片子用起来还是不错. 内部的18K,还做了dataflash 模拟eeprom . IO的使用模式与M051相差无几. 输入, opendrain,push-pull, 准双向. 用过新唐M0,再用这些再爽不过了.
     言归正传, 主要是设计了一个N76E003通过spi口驱动adxl345加速度传感器, 然后通过uart0串口通过485芯片,远程接收modbus RTU主机的查询;

                                             
/*******************************************************************************
* FUNCTION_PURPOSE: Main function
******************************************************************************/
void main (void)
{
    /* 系统初始化*/
    SYS_init();
    TMR0_Init();
    TMR1_Init();
    TMR2_Init();
   
    /* 初始化串口 */
    InitialUART0_Timer3(9600);
    //SPI_Initial();
   
    /* 看门狗初始化 */
    WDT_Initial();
   
    /*  开中断 */
    sei();
   
    /* 测试程序 */

   
    /* APP程序初始化 */
    LED_operate (_LED1_, ON);///点亮工作指示灯;
    LED_flash   (_LED1_, ON);
   
    /*  初始化adxl345 */
    SYS_adxl_buf[0] = 0x0b;
    WriteToADXL345ViaSpi(XL345_DATA_FORMAT,1,SYS_adxl_buf);
   
    SYS_adxl_buf[0] = 0x08;
    WriteToADXL345ViaSpi(XL345_POWER_CTL,1,SYS_adxl_buf);
   
    ReadFromADXL345ViaSpi(XL345_DEVID,1,SYS_adxl_buf);
   
    SYS_adxl345_deviceid = SYS_adxl_buf[0];
    /****** 主循环 *******/
    while(1)
    {
        if( f20ms)
        {
            f20ms =0;
            /* 读取三轴的数据值, 13bit分辨率, 量程+-16g */
            ReadFromADXL345ViaSpi(XL345_DATAX1,1,SYS_adxl_buf);
            SYS_x_data = SYS_adxl_buf[0];
            SYS_x_data = SYS_x_data<<8;
            ReadFromADXL345ViaSpi(XL345_DATAX0,1,SYS_adxl_buf);
            SYS_x_data = SYS_x_data +SYS_adxl_buf[0];

            ReadFromADXL345ViaSpi(XL345_DATAY1,1,SYS_adxl_buf);
            SYS_y_data = SYS_adxl_buf[0];
            SYS_y_data = SYS_y_data<<8;
            ReadFromADXL345ViaSpi(XL345_DATAY0,1,SYS_adxl_buf);
            SYS_y_data = SYS_y_data +SYS_adxl_buf[0];


            ReadFromADXL345ViaSpi(XL345_DATAZ1,1,SYS_adxl_buf);
            SYS_z_data = SYS_adxl_buf[0];
            SYS_z_data = SYS_z_data<<8;
            ReadFromADXL345ViaSpi(XL345_DATAZ0,1,SYS_adxl_buf);
            SYS_z_data = SYS_z_data +SYS_adxl_buf[0];

                     
        
        }
   
        /* 3- MODBUS_RTU通信处理 */
        mod_process();
   
        /* x-喂狗 */
        WDT_reset();
    }
    /****** 主循环 *******/
       
               
}

沙发
Harvard|  楼主 | 2017-7-4 22:37 | 只看该作者
/****************************************************************************************
  Copyright (C), 1988-1999, Huawei Tech. Co., Ltd.
  File name:      usart.c
  Author:         Harvard
  Version:        0.0
  Date: 20070620
  Description:  完成串行口的初始化,设置,提供单字节的发送接收
  History:
  ----------------------------------------------------------------------------------------   
  1. Date: 20070622
     Author:        XXJ
     Modification:
        t1.5与t3.5采用固定间隔代替,分别为3
  2. date: 20070707
  author: xxj
        abs: 顺利完成modbus的读参数功能,且调试时可以通过在代码中
        用串口发送不同状态码的方式来判定各种通信错误:如0位crc
校验出错;1为通信地址错误.
针对16位变量和8位变量在位地址处理上的异同.把所有的
        变量均统一成8位一个字节进行同一处理.只接收字地址寻址
        收到字地址后.立即乘以2,变成字节,对于16位变量,读两个
        字节恰好全部读取,而对于8位变量,可能将其他变量的8个位
        也读取了,组成一个字节,这样会造成每次读取时,由于其他
        变量的变化而每次的值都会不一样,解决办法时,尽可能将
        两个8位变量组合成一个16位的状态字,由用户去统一协调
        提取.还有一个办法是在message ,map 中,8位变量前面加入

*****************************************************************************************/
#include "config.h"

/*
* 快读系统状态
* 返回:系统的软件.硬件版本可用于系统的485硬件通断测试
*/

/* 函数名称:void mod_process(void)
* 输入: 接收结束标志
* 输出:
*/

///////////////////////////////////////////////
u16 SYS_485_adr = 1 ;
u16 SYS_485_baud ;/*Meaasga maps */
static u08 Space = 0;
static u16 Space16 = 0;

/* Modbus地址与实际内部变量的映射表 */
u08 * code modbus_tab[]   =
{
      //只读
&HIGH_(SYS_485_adr),                &LOW_(SYS_485_adr),         //0: 本机地址      
&HIGH_(SYS_x_data),                 &LOW_(SYS_x_data),         //1: 本机地址
&HIGH_(SYS_y_data),           &LOW_(SYS_y_data),         //2
        &HIGH_(SYS_z_data),           &LOW_(SYS_z_data),         //3
        &HIGH_(SYS_485_adr),                &LOW_(SYS_485_adr),         //4: 本机地址      
&HIGH_(SYS_baudrate),               &LOW_(SYS_baudrate),     //5: 本机地址      
&HIGH_(Space16),             &LOW_(Space16), //3
        &HIGH_(Space16),             &LOW_(Space16), //4

        &HIGH_(Space16),             &LOW_(Space16),         //5
        &Space,                         &LOW_(Space16), //6
        &Space,                         &LOW_(Space16), //7
        &Space,                         &LOW_(Space16 ), //8
        &Space,                         &LOW_(Space16 ),      //9


};

/* 针对HLM_SOKO的需求, 构造特殊的传输帧,
*  数据经过0x10指令 ,类似于广播地址般的直接写入
*  从机
*/
////////////////////////单路默认:左通道
void modbus_wr( void )
{
    u08 i;
    //////////////////////////1-帧头
   
    ////4 计算CRC ,后放在RxBuf[20..21]中

    CRC = 0xffff;
    for( i=0; i<20 ; i++ )
    {
        CRC = _crc16_update( CRC ,TxBuf );
    }
    TxBuf[20] = CRC%256;
    TxBuf[21] = CRC/256;

    TxBufPtr = 21;
    send_frame ();

}

////////////////////////单路默认:左通道
void modbus_wr_R( void )
{
    u08 i;
    //////////////////////////1-帧头

   
  
   
    ////4 计算CRC ,后放在RxBuf[20..21]中

    CRC = 0xffff;
    for( i=0; i<20 ; i++ )
    {
        CRC = _crc16_update( CRC ,TxBuf );
    }
    TxBuf[20] = CRC%256;
    TxBuf[21] = CRC/256;

   
    TxBufPtr = 21;
    send_frame ();

}

void mod_process(void)
{
        if( fFrameDone == OK ) //接收已经完成
{
fFrameDone = NO;         //清除标志

usart_dis_recv(); //禁止接收中断

if ( fFrameBad ==TRUE )     //数据帧被破换,不予处理
{
fFrameBad = FALSE;     //清除错误标志
}

else //数据帧正常则解析协议
{
cmd_proc();
        }

        RxBufPtr   = RX_BUF_BGN_ADR;//准备下次接收

usart_en_recv();         //重新接收

}

if ( fRxBufFull == YES )      //数据帧错误
{
fFrameBad  =  FALSE;     //清除错误标志

         RxBufPtr   =  RX_BUF_BGN_ADR; //准备下次接收
}
fRxBufFull =  NO   ;     //清空缓冲区

usart_en_recv();           //重新接收

}

/*命令处理*/
void cmd_proc(void)
{
        u16 reg_adr =0; //临时 存放要读写的寄存器的地址
u08 reg_errcode =0;//出错时的错误编码  

////////////////////////////////////////////=======================CRC 校验
if ( CRC != 0 )                           
        {
        //usart_send_byte(0);    //错误代码0: crc 校验错误
return ;
        }
        CRC = 0xffff; //准备计算crc

        ///////////////////////////////////////////========================判断地址是否正确
if( SYS_485_adr != RxBuf[0] && 0 != RxBuf[0] )
        {
        return ;
        }


        TxBuf[0] = SYS_485_adr ;//respond frame's header
        TxBuf[1] = RxBuf[1];//respond frame's function code
        CRC      = _crc16_update( CRC ,TxBuf[0] );
        CRC      = _crc16_update( CRC ,TxBuf[1] );
        ////////////////////////////////////////// ========================识别功能码
if( RxBuf[1]==0x07 )                      //----------------fast read version
        {
        TxBuf[2]= (HVERSION<<4)+SVERSION;
        CRC = _crc16_update( CRC ,TxBuf[2] );
        TxBuf[3] = CRC%256;
        TxBuf[4] = CRC/256;
        TxBufPtr = 4;
        send_frame ();
        }
        else
        if( RxBuf[1]==0x03 || RxBuf[1]== 0x04 ) //-------------------------读寄存器
{
u08  cnt;
TxBuf[2]   = RxBuf[5]*2; //字节数
CRC = _crc16_update( CRC ,TxBuf[2] );
TxBufPtr   = 3;    //调整发送指针,
for( cnt =0 ; cnt< TxBuf[2] ; )//RxBuf[5]--word counts
        {
            TxBuf[TxBufPtr] = *((u08 *) modbus_tab [cnt +RxBuf[3]*2]);//RxBuf[3]中为起始地址低8位
CRC         = _crc16_update( CRC ,TxBuf[TxBufPtr] );
        cnt         =  cnt + 1;
        TxBufPtr    =  TxBufPtr + 1  ;
        }
        TxBuf[TxBufPtr] = CRC%256;
        TxBufPtr        = TxBufPtr + 1; //调整发送指针
TxBuf[TxBufPtr] = CRC/256;
        send_frame();//发送
}
else
        if( RxBuf[1]==0x10 ) //--------------------写寄存器
{
u08  cnt;
             TxBuf[2]   = RxBuf[2];   //写起始地址高8位,一般为0
             TxBuf[3]   = RxBuf[3]; //写起始地址低8位
     TxBuf[4]   = RxBuf[4];   //欲写寄存器数目高8位,一般为0
             TxBuf[5]   = RxBuf[5]; //欲写寄存器数目低8位

//不允许写长度为0
        if(RxBuf[6] < 2) goto err_ret;
        //通信协议的规范性检测,从数据帧长度判断
if( (6 + 2 +  RxBuf[6] )  != RxBufPtr )  goto err_ret;//指令格式不正确

//是否超出通信协议的地址表范围,所有参数共26字节
if( (  RxBuf[5] + RxBuf[3]  )  >20 )    goto err_ret;//字地址及数目超范围
// 20070713 :考虑安全性,将11 12号地址的写功能关闭

    //
        // if( (RxBuf[3] <= 9 )) goto err_ret;//10号子地址以下为只读

for( cnt =0 ; cnt< RxBuf[6] ;cnt++ )     //RxBuf[6]--byte counts
        {
             
          *((u08 *) modbus_tab[ cnt +RxBuf[3]*2] )
   
        =

        RxBuf[ 7 + cnt ];     //RxBuf[3]中为起始地址低8位,乘以2,是将之转换为内部的字节地址

}

for ( cnt = 0 ; cnt < 4 ; cnt ++ ) //发送字节指针为0-7,其中:0 .1 为固定,其crc已经计算好
{
            CRC = _crc16_update( CRC ,TxBuf[2+cnt] );//2-5
        }
        TxBuf[6] = CRC%256;
        TxBuf[7] = CRC/256;
        TxBufPtr = 7; //调整发送指针
send_frame(); //发送

//SynE2promFromRam();   //保存更改

    usart_init (SYS_485_baud);//重新初始化串行口
//通信参数的写操作功能已经被关闭

err_ret: TxBufPtr = TX_BUF_BGN_ADR;    //after sending ,reset ptr
        }
   
   
    else
        if( RxBuf[1]==0x06 ) //----------写寄存器(单个寄存器)
        {
        /* 先获取寄存器地址 */
reg_adr =((u16) RxBuf[2]<<8) + (u16) RxBuf[3] ; // reg_adr = (u16) RxBuf[3] ;

        /* 最大写入地址限定 */
        if(reg_adr > MODBUS_ADDR_MAX )
        {
        reg_errcode = 2;//非法的数据地址

goto err_ret_06;
        }
        /* 预写一次寄存器值
    */
        *((u08 *)(modbus_tab [reg_adr*2]))=  RxBuf[4] ;
        *((u08 *)(modbus_tab [reg_adr*2+1]))=RxBuf[5] ;
         
        ///////发送会送数据帧
        TxBuf[0] = RxBuf[0];
        TxBuf[1] = RxBuf[1];
        TxBuf[2] = RxBuf[2];
        TxBuf[3] = RxBuf[3];
        TxBuf[4] = RxBuf[4];
        TxBuf[5] = RxBuf[5];
        TxBuf[6] = RxBuf[6];
        TxBuf[7] = RxBuf[7];
        TxBufPtr = 7; //调整发送指针
        send_frame(); //发送

        ///////保存参数
        //save_user_data_to_flash();   //保存更改
        
        /////////////错误处理:
        err_ret_06:
        /* 如果是非法的数据地址 */
        if( reg_errcode == 2 )
        {
            TxBuf[0] = RxBuf[0];
            TxBuf[1] = 0x86;
            CRC =0xffff;
            CRC      = _crc16_update( CRC ,TxBuf[0] );
            CRC      = _crc16_update( CRC ,TxBuf[1] );
            TxBuf[2] = reg_errcode;
            CRC      = _crc16_update( CRC ,TxBuf[2] );
            TxBuf[3] = CRC%256;
            TxBuf[4] = CRC/256;
            TxBufPtr = 4;
            send_frame();
        }
        else
        if( reg_errcode == 3 )
        {
            TxBuf[0] = RxBuf[0];
            TxBuf[1] = 0x86;
            CRC =0xffff;
            CRC      = _crc16_update( CRC ,TxBuf[0] );
            CRC      = _crc16_update( CRC ,TxBuf[1] );
            TxBuf[2] = reg_errcode;
            CRC      = _crc16_update( CRC ,TxBuf[2] );
            TxBuf[3] = CRC%256;
            TxBuf[4] = CRC/256;
            TxBufPtr = 4;
            send_frame();
        }
            
        TxBufPtr = TX_BUF_BGN_ADR;    //after sending ,reset ptr         
    }


}

/*发送一一帧数据*/
void send_frame(void)
{
        u08  i;
        for( i = 0 ; i <= TxBufPtr; i++ ) //TxBufPtr为发送缓冲区指示器
{
usart_send_byte(TxBuf);
        }
        TxBufPtr = TX_BUF_BGN_ADR;    //after sending ,reset ptr
}






/****************************************************************************************
  Copyright (C), 1988-1999, Huawei Tech. Co., Ltd.
  File name:      usart.c
  Author:         Harvard
  Version:        0.0
  Date: 20070620
  Description:  完成串行口的初始化,设置,提供单字节的发送接收
  History:
  ----------------------------------------------------------------------------------------   
  1. Date: 20070622
     Author: XXJ
     Modification:
        t1.5与t3.5采用固定间隔代替,分别为3
  2. ...
  ----------------------------------------------------------------------------------------   
  1. Date: 2016-03-03
     Author:  Shine
     Abstract: - 升级串口初始化函数,改串口晶振为外部12MHZ,波特率范围从4800-115200bps
               - 串口初始化i函数的参数从16bit升级到32bit
  
*****************************************************************************************/
#include "config.h"


u16  _crc16_update(u16 crc, u08 a)
{
        int i;
        crc ^= a;
        for (i = 0; i < 8; ++i)
        {
            if (crc & 1)
        crc = (crc >> 1) ^ 0xA001;
            else
        crc = (crc >> 1);
        }
        return crc;
}

/*
** 串行口初始化
** baudrate取值为: 4800 9600 19200
*/

void usart_init(u32 baudrate)
{
    /* 根据实际需要,选择不同的定时器作为波特率发生器 */
    InitialUART0_Timer3(baudrate);
    /* 将485引脚置为接收状态 */
    TXD_EN = 0;
    /* 使能串口中断0*/
    set_ES;
}

void usart_dis_recv(void)
{
    /* 禁止接收 */
    TXD_EN =1;
    /* 关闭串口0中断 ;*/
    clr_ES;
}
void usart_en_recv (void)
{
    /* 使能接收引脚*/
    TXD_EN = 0;
    /* 打开串口中断*/
    set_ES;
}

/*超时定时器初始化及其开关控制*/
#define T2_TICK_MIN 1000000/(F_CPU/1024)
#define T2_TICK 1000 //1ms
#define TIMER2_BGN_VAL (255-T2_TICK/T2_TICK_MIN)

//static u08 CNT_t15=0;         //1.5字符时间
//static u08 CNT_t35=0;         //3.5字符时间
static  u08 SYS_cnt_frame   =0;     //时间间隔计数器,用于1.5 和3.5字符时间判断
        u08 SYS_t2_on       = OFF ;     //用于1.5 3.5字符判断的软定时器 开关.



void timer2_on(void)
{
   
    SYS_cnt_frame = 0 ;
    SYS_t2_on = ON;
}
void timer2_off(void)
{
    SYS_t2_on = OFF;
    /** 帧间隔控制 */
    SYS_cnt_frame = 0;
//    CNT_t15= 0;
//    CNT_t35= 0;
}

/**
* @brief       Timer-2 IRQ
*
* @param       None
*
* @return      None
*
* @Details     The TIMER2 default IRQ, declared in startup_M051Series.s.
*/
void Timer2_ISR (void) interrupt 3
{
    //////////////////////////////////////////////--重新加载定时器初值
     /* 给TIMER 赋初值
     ** 定时器时钟为:16MHZ/12= 4/3MHZ
     ** 定时器tick为: 1/ (4/3MHZ) = 3/4us = 0.75uS
     ** 定时器要定时1ms:
     ** 定时器初值为: 1000us/0.75uS = 1333
     */
    TH1 = (65535-1333)/256;
    TL1 = (65535-1333)%256;   


    /* 如果定时器打开 */
    if( SYS_t2_on == ON )
   {

          SYS_cnt_frame++;
         
          if ( SYS_cnt_frame > T35) //a frame was finished
          {
               usart_dis_recv();   //禁止接收中断

               timer2_off ()   ;   //tunr off timer;

               SYS_cnt_frame = 0 ;   //reset timer

               RxBufPtr--; //调整指针

               fFrameDone    = OK;   //frame is completed
           }      
    }
    /* 定时器关,还未进行帧间隔判断 */
   else
   {
        SYS_cnt_frame = 0;
   }
   
}



u08 RxBuf[MAX_BUF_LEN]={0,1,2,3,4};
u08 TxBuf[MAX_BUF_LEN];
u08 RxBufPtr = RX_BUF_BGN_ADR;
u08 TxBufPtr = TX_BUF_BGN_ADR;
BOOL fIsRXING = NO;          //is receiving now
BOOL fIsTXING = NO;          //is transsending now
BOOL fRxBufFull = NO;
BOOL fTxBufFull = NO;
BOOL fFrameDone = NO;    //one frame finished
BOOL fFrameBad  = NO;    //frame was broken
u16  CRC = 0xFFFF;    //CRC初始值


/*接收中断*/
void UART0_IRQHandler( void ) interrupt 4
{
    /* 1 - 接收中断处理 */
    if ( RI==1 )
    {                                       /* if reception occur                       */
        clr_RI;                             /* clear reception flag for next reception  */

        /* Rx trigger level is 1 byte*/   
       RxBuf[RxBufPtr] = SBUF;

        /* reset timer */
       SYS_cnt_frame = 0 ;   

        /*new frame coming*/
        if( RxBufPtr == RX_BUF_BGN_ADR )//新的一帧开始
        {
            CRC = 0xffff ;             //准备crc计算,

            timer2_on();                    //开启定时器计时
        }

        /*with a frame and RxBuf is not full*/
        /*when we detect a frame was borken according to t15 ,
        * we keep on receiving in order to synchronize the frame  
        */
        else if
          ( RxBufPtr < MAX_BUF_LEN -1)
        {  
            if ( SYS_cnt_frame > T15 && SYS_cnt_frame < T35 )
            {
                 SYS_cnt_frame = 0 ;   //reset timer
            
                 fFrameBad     = TRUE; //frame is broken
            }
        }

        /*RxBuf full */
        /*
        **
        */
        else if( RxBufPtr >= MAX_BUF_LEN -1)
        {
            usart_dis_recv()    ; //stop receving
        
            timer2_off ()     ;            //tunr off timer;

            fRxBufFull   = YES ;            //缓冲区已满
           
            fFrameBad     = TRUE;            //frame is broken
           
            RxBufPtr --;              //指针保持不变,防止破坏其他数据区
        }
            CRC = _crc16_update( CRC,RxBuf[RxBufPtr] );

            RxBufPtr++; //adjust pointer
    }//end while

    /* 2 - 发送标记处理 */
    if(TI==1)
    {
        clr_TI;                             /* if emission occur */
    }
}

/*查询发送*/
void usart_send_byte( u08 txbyte )
{
    TXD_EN      =1;
    WORK_LED    =0;
    /* 向串口写入1字节   */
    Send_Data_To_UART0( txbyte );
    /* 等待1字节传输完毕 */
    TXD_EN      =0;
    WORK_LED    =1;
}



使用特权

评论回复
板凳
Harvard|  楼主 | 2017-7-4 22:38 | 只看该作者











adxl345的spi驱动程序;



/**
  ******************************************************************************
  * [url=home.php?mod=space&uid=288409]@file[/url]    DRV_spi.c
  * [url=home.php?mod=space&uid=187600]@author[/url]  Shine
  * [url=home.php?mod=space&uid=895143]@version[/url] V1.0
  * [url=home.php?mod=space&uid=212281]@date[/url]    2017-06-29
  * @brief   硬件看门狗功能
  ******************************************************************************
  * @attention
  *
  *
  ******************************************************************************
  */
/* 加载官方头文件 */
#include "config.h"

//////////////////////////////////////////////////////////==硬件spi
//----------------------------------------------------------------------------
void SPI_Initial(void)
{      
    //P15_Quasi_Mode;     // P15 (SS) Quasi mode
    P10_Quasi_Mode;     // P10(SPCLK) Quasi mode
    P00_Quasi_Mode;     // P00 (MOSI) Quasi mode
    P01_Quasi_Mode;     // P22 (MISO) Quasi mode
   
    set_DISMODF;        // SS General purpose I/O ( No Mode Fault )
    clr_SSOE;   
   
    clr_LSBFE;          // MSB first         
   
    clr_CPOL;           // The SPI clock is low in idle mode
    set_CPHA;           // The data is sample on the second edge of SPI clock
        
    set_MSTR;           // SPI in Master mode
    SPICLK_DIV16;       // Select SPI clock
    set_SPIEN;          // Enable SPI function
    clr_SPIF;
}


void delay (signed int length)
{
        while (length >0)
            length--;
}

//---------------------------------
//WriteToADXL345ViaSpi();
//---------------------------------
//Function that writes to the ADXL345 via the SPI port. It sends first the control
//word that includes the start address and then the data to write.
//--------------------------------------------------------------------------------
void WriteToADXL345ViaSpi(unsigned char RegisterAddress, unsigned char NumberofRegisters, unsigned char *RegisterData)
{
        unsigned char ControlValue = 0;
        unsigned char ValueToWrite = 0;
        signed char RegisterIndex = 0;
        unsigned char i = 0;

        //Create the 8-bit header
        ControlValue = RegisterAddress;

// SET_SCL();
// delay(1);
        SET_CS();
        SET_SCL();
        delay(3);
        CLR_CS(); //bring CS low
        delay(3);

        CLR_SCL();
        CLR_SDO(); //set WRITE bit
        delay(3);
        SET_SCL();
        delay(3);
        ControlValue <<= 1;

        CLR_SCL();
        SET_SDO(); //set MB bit
        delay(3);
        SET_SCL();
        delay(3);
        ControlValue <<= 1;

        //Write out the control word
        for(i=0; i<6; i++)
        {
        CLR_SCL();
        if(0x80 == (ControlValue & 0x80))
        {
        SET_SDO();   //Send one to SDO pin
        }
        else
        {
        CLR_SDO();   //Send zero to SDO pin
        }
        delay(3);
        SET_SCL();
        delay(3);
        ControlValue <<= 1; //Rotate data
        }

        //And then the data
        for (RegisterIndex=NumberofRegisters; RegisterIndex>0; RegisterIndex--)
        {
        ValueToWrite = *(RegisterData + RegisterIndex - 1);
        for (i=0; i<8; i++)
        {
        CLR_SCL();
        if(0x80 == (ValueToWrite & 0x80))
        {
        SET_SDO();   //Send one to SDO pin
        }
        else
        {
        CLR_SDO();   //Send zero to SDO pin
        }
        delay(3);
        SET_SCL();
        delay(3);
        ValueToWrite <<= 1; //Rotate data
        }
        }
        SET_CS(); //bring CS high again
}

//---------------------------------
//ReadFromADXL345ViaSpi();
//---------------------------------
//Function that reads from the ADXL345 via the SPI port. It first send the control word
//that includes the start address and then 8 clocks for each register to read.
//--------------------------------------------------------------------------------
void ReadFromADXL345ViaSpi(unsigned char RegisterAddress, unsigned char NumberofRegisters, unsigned char *RegisterData)
{
        unsigned char ControlValue = 0;
        signed char RegisterIndex = 0;
        unsigned char ReceiveData = 0;
        unsigned char i = 0;
        unsigned int iTemp = 0;

        //Create the 8-bit header
        ControlValue = RegisterAddress;

// SET_SCL();
// delay(1);
        SET_CS();
        SET_SCL();
        delay(3);
        CLR_CS(); //bring CS low
        delay(3);

        CLR_SCL();
        SET_SDO(); //set READ bit
        delay(3);
        SET_SCL();
        delay(3);
        ControlValue <<= 1;

        CLR_SCL();
        SET_SDO(); //set MB bit
        delay(3);
        SET_SCL();
        delay(3);
        ControlValue <<= 1;

        //Write out the control word
        for(i=0; i<6; i++)
        {
        CLR_SCL();
        if(0x80 == (ControlValue & 0x80))
        {
        SET_SDO();   //Send one to SDO pin
        }
        else
        {
        CLR_SDO();   //Send zero to SDO pin
        }
        delay(3);
        SET_SCL();
        delay(3);
        ControlValue <<= 1; //Rotate data
        }

        //Read data in
        for (RegisterIndex=NumberofRegisters; RegisterIndex>0; RegisterIndex--)
        {
        for(i=0; i<8; i++)
        {
        CLR_SCL();
        ReceiveData <<= 1; //Rotate data
        delay(3);
        SET_SCL();
        //iTemp = GP1DAT; //Read SDI of ADXL345
        if(ADXL345_SDI)    //SDI  P1.5
        {
        ReceiveData |= 1;
        }
        delay(2);
        }
        *(RegisterData + RegisterIndex - 1) = ReceiveData;
        }
        SET_CS(); //bring CS high again
}




dataflash 模拟eeprom 官方的例程有点小问题. 是利用了固定外部128-256的地址. 但是容易与其他变量的地址冲突. 我改成了固定数组.



/**
  ******************************************************************************
  * @file    DRV_dataflash.c
  * @author  Shine
  * @version V1.0
  * @date    2017-06-29
  * @brief   利用flash,模拟eeprom,作为掉电存储,全部设置为APROM,APROM的最后2K空间
  *          (3800-47FFh)
  ******************************************************************************
  * @attention
  *
  *
  ******************************************************************************
  */


/*---------------------------------------------------------------------------------------------------------*/
/*                                                                                                         */
/* Copyright(c) 2017 Nuvoton Technology Corp. All rights reserved.                                         */
/*                                                                                                         */
/*---------------------------------------------------------------------------------------------------------*/

//***********************************************************************************************************
//  Website: http://www.nuvoton.com
//  E-Mail : MicroC-8bit@nuvoton.com
//  Date   : Jan/21/2017
//***********************************************************************************************************

//***********************************************************************************************************
//  File Function: N76E003 APROM program DATAFLASH as EEPROM way
//***********************************************************************************************************
#include "config.h"


/*****************************************************************************************************************
write_DATAFLASH_BYTE :
user can copy all this subroutine into project, then call this function in main.
******************************************************************************************************************/
unsigned char xdata flashram[128];
void write_DATAFLASH_BYTE(UINT16 u16_addr,UINT8 u8_data)
{
        u16 looptmp=0;
    u08 u8_addrl_r;
        unsigned char code *cd_longaddr;
        u08 xdata *xd_tmp;

///////////Check page start address:检查要写入的地址,应该是0-128的pagesize

        u8_addrl_r = u16_addr;//Shine: 获取要写入地址的低8位,根据是否大于128 完成页面的强制对齐
if (u8_addrl_r<0x80)
        {
        u8_addrl_r = 0;
        }
        else
        {
        u8_addrl_r = 0x80;
        }
   
///////////Save APROM data to XRAM:
        //xd_tmp = 0x80;        /// 原来写法,直接利用xdata的0x80-0x100来作为缓存有风险,应该用固定内存
xd_tmp = &flashram[0];  /// 改为由系统分配的临时缓存的首地址,当然,存在内存的资源浪费
cd_longaddr = (u16_addr&0xff00)+u8_addrl_r; ///获取要写入的flash地址
//while (xd_tmp !=0x100)
        while (xd_tmp !=(&flashram[0]+0x80) )///从基址开始依次读取128byte
        {
        *xd_tmp = *cd_longaddr;
        looptmp++;
        xd_tmp++;
        cd_longaddr++;
        }
   
/////////Modify customer data in XRAM
/// 官方的库写法,但是个人认为有隐患, 0x80的xram可能会被系统分配;
// u8_addrl_r = u16_addr; ///获取写入地址,分: <128 ;和大于128两种情况
// if (u8_addrl_r<0x80)   ///写入地址<128的,直接从xram的0x80,也就是128开始写入xram
// {
// xd_tmp = u8_addrl_r+0x80;
// }
// else                  ///大于128的,也是从128后的地址开始写入,只是不在需要+128的偏置.
// {
// xd_tmp = u8_addrl_r+0;
// }
// *xd_tmp = u8_data;///地址修正后,直接写入
   
u8_addrl_r = u16_addr;
   
    /* 将写入地址0-256:变成0-128;因为xram混存仅为128*/
        if (u8_addrl_r<0x80)
        {
        xd_tmp =  &flashram[0] + u8_addrl_r;
        }
        else
        {
        xd_tmp =  &flashram[0] +(u8_addrl_r-0x80);
        }
        *xd_tmp = u8_data;
        
   
////////Erase APROM DATAFLASH page
        IAPAL = u16_addr;   ///获取要写入地址的低位和高位;
IAPAH = u16_addr>>8;///
        IAPFD = 0xFF;
        set_IAPEN;
        set_APUEN;
        IAPCN = 0x22;
        set_IAPGO;
        
/////////Save changed RAM data to APROM DATAFLASH
// u8_addrl_r = u16_addr;
// if (u8_addrl_r<0x80)
// {
// u8_addrl_r =0;
// }
// else
// {
// u8_addrl_r = 0x80;
// }
//        xd_tmp = 0x80;
//        IAPAL = u8_addrl_r;
//        IAPAH = u16_addr>>8;
//        set_IAPEN;
//        set_APUEN;
//        IAPCN = 0x21;
// while (xd_tmp !=0xFF)
// {
// IAPFD = *xd_tmp;
// set_IAPGO;
// IAPAL++;
// xd_tmp++;
// }
//        
    /* 128字节依次写入 flash */
        u8_addrl_r = u16_addr;
        if (u8_addrl_r<0x80)
        {
        u8_addrl_r =0;
        }
        else
        {
        u8_addrl_r = 0x80;
        }
        xd_tmp =   &flashram[0] ;
        IAPAL = u8_addrl_r;
        IAPAH = u16_addr>>8;
        set_IAPEN;
        set_APUEN;
        IAPCN = 0x21;
        while (xd_tmp !=(&flashram[0]+0x80) )
        {
        IAPFD = *xd_tmp;
        set_IAPGO;
        IAPAL++;
        xd_tmp++;
        }
        
     
        clr_APUEN;
        clr_IAPEN;
}

//-------------------------------------------------------------------------
UINT8 read_APROM_BYTE(UINT16 code *u16_addr)
{
        UINT8 rdata;
        rdata = *u16_addr>>8;
        return rdata;
}

/******************************************************************************************************************/



//void main (void)
//{
// UINT8 datatemp;
///* -------------------------------------------------------------------------*/
///*  Dataflash use APROM area, please ALWAYS care the address of you code    */
///* APROM 0x3800~0x38FF demo as dataflash             */
///* Please use Memory window key in C:0x3800 to check earse result */      
///* -------------------------------------------------------------------------*/
// InitialUART0_Timer1(115200);
////call write byte
// write_DATAFLASH_BYTE (0x3881,0x55);
// write_DATAFLASH_BYTE (0x3882,0x56);
// write_DATAFLASH_BYTE (0x3855,0xaa);
// write_DATAFLASH_BYTE (0x3856,0x66);
////call read byte
// datatemp = read_APROM_BYTE(0x3882);

//    while(1)
// {
//// printf ("\n data temp = 0x%bx", datatemp);
// }
//}



#ifndef _DRV_SPI_H
#define _DRV_SPI_H

void SPI_Initial(void);
void ReadFromADXL345ViaSpi(unsigned char RegisterAddress, unsigned char NumberofRegisters, unsigned char *RegisterData);
void WriteToADXL345ViaSpi(unsigned char RegisterAddress, unsigned char NumberofRegisters, unsigned char *RegisterData);

#define SET_CS() ADXL345_CS =1
#define CLR_CS() ADXL345_CS =0

#define SET_SCL() ADXL345_SCLK =1
#define        CLR_SCL() ADXL345_SCLK =0

#define SET_SDO() ADXL345_SDO = 1
#define CLR_SDO() ADXL345_SDO = 0

#endif

使用特权

评论回复
地板
huangcunxiake| | 2017-7-4 23:48 | 只看该作者
给力,哟图有真相。

使用特权

评论回复
5
Harvard|  楼主 | 2017-7-5 11:07 | 只看该作者
16MHZ内部晶振 , 通信在115200 ,非常稳定 .内部晶振的精度不错.

使用特权

评论回复
6
yiyigirl2014| | 2017-7-18 19:13 | 只看该作者
这个价格真是给力。

使用特权

评论回复
7
Harvard|  楼主 | 2017-7-19 15:41 | 只看该作者
价格不错 片子很好用 .目前就是有个小问题. 在线调试的时候, watch里面的变量不会自动更新. 暂停以后才会变化. 已经勾选了 periodical.... 这个选项.请高手指教.

使用特权

评论回复
8
Harvard|  楼主 | 2017-7-20 14:22 | 只看该作者
来个实物图;


再上个接到上位机的:



使用特权

评论回复
9
yiyigirl2014| | 2017-7-20 20:46 | 只看该作者
前天我一个朋友开发了个产品,量产了三万,用的就是这个MCU。

使用特权

评论回复
10
lss1985| | 2017-8-2 15:59 | 只看该作者
DRV_dataflash.c这个在哪个BSP库里?

使用特权

评论回复
11
Harvard|  楼主 | 2017-8-5 11:59 | 只看该作者
照抄就行 自己写的

使用特权

评论回复
12
wang0225| | 2017-8-23 15:24 | 只看该作者
能给分源码工程不,楼主

使用特权

评论回复
13
Harvard|  楼主 | 2017-8-24 22:28 | 只看该作者
可以的

使用特权

评论回复
14
wang0225| | 2017-8-25 08:43 | 只看该作者

使用特权

评论回复
15
Harvard|  楼主 | 2017-9-7 08:25 | 只看该作者
源码已经发送 希望对感兴趣的朋友有用.

使用特权

评论回复
16
Harvard|  楼主 | 2017-9-7 08:25 | 只看该作者
P.S 谁能解决调试003时, 在watch窗口中的变量不能自动更新的问题,必须要暂停调试 才可以看到变化 郁闷的

使用特权

评论回复
17
挖东衣宇| | 2017-9-7 11:12 | 只看该作者
可以分享一下源码吗,1119029110@qq.com,想学习一下前辈的经验,感谢!

使用特权

评论回复
18
Harvard|  楼主 | 2017-9-9 19:20 | 只看该作者

使用特权

评论回复
19
mintspring| | 2017-9-10 17:06 | 只看该作者
485确实用的挺多的,传的比232选多了

使用特权

评论回复
20
newphj| | 2017-9-13 09:04 | 只看该作者
你好,是使用keil C51编译的吗?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:单片机工作者

74

主题

1726

帖子

21

粉丝