[应用方案] 基于新唐N76E003的三轴加速度485远传系统(modbusRTU)

[复制链接]
15498|82
 楼主| Harvard 发表于 2017-7-4 22:23 | 显示全部楼层 |阅读模式
本帖最后由 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主机的查询;

                                           M@9SOV{{C9QD0)CUU8HG8{V.png   
  1. /*******************************************************************************
  2. * FUNCTION_PURPOSE: Main function
  3. ******************************************************************************/
  4. void main (void)
  5. {
  6.     /* 系统初始化*/
  7.     SYS_init();
  8.     TMR0_Init();
  9.     TMR1_Init();
  10.     TMR2_Init();
  11.    
  12.     /* 初始化串口 */
  13.     InitialUART0_Timer3(9600);
  14.     //SPI_Initial();
  15.    
  16.     /* 看门狗初始化 */
  17.     WDT_Initial();
  18.    
  19.     /*  开中断 */
  20.     sei();
  21.    
  22.     /* 测试程序 */

  23.    
  24.     /* APP程序初始化 */
  25.     LED_operate (_LED1_, ON);///点亮工作指示灯;
  26.     LED_flash   (_LED1_, ON);
  27.    
  28.     /*  初始化adxl345 */
  29.     SYS_adxl_buf[0] = 0x0b;
  30.     WriteToADXL345ViaSpi(XL345_DATA_FORMAT,1,SYS_adxl_buf);
  31.    
  32.     SYS_adxl_buf[0] = 0x08;
  33.     WriteToADXL345ViaSpi(XL345_POWER_CTL,1,SYS_adxl_buf);
  34.    
  35.     ReadFromADXL345ViaSpi(XL345_DEVID,1,SYS_adxl_buf);
  36.    
  37.     SYS_adxl345_deviceid = SYS_adxl_buf[0];
  38.     /****** 主循环 *******/
  39.     while(1)
  40.     {
  41.         if( f20ms)
  42.         {
  43.             f20ms =0;
  44.             /* 读取三轴的数据值, 13bit分辨率, 量程+-16g */
  45.             ReadFromADXL345ViaSpi(XL345_DATAX1,1,SYS_adxl_buf);
  46.             SYS_x_data = SYS_adxl_buf[0];
  47.             SYS_x_data = SYS_x_data<<8;
  48.             ReadFromADXL345ViaSpi(XL345_DATAX0,1,SYS_adxl_buf);
  49.             SYS_x_data = SYS_x_data +SYS_adxl_buf[0];

  50.             ReadFromADXL345ViaSpi(XL345_DATAY1,1,SYS_adxl_buf);
  51.             SYS_y_data = SYS_adxl_buf[0];
  52.             SYS_y_data = SYS_y_data<<8;
  53.             ReadFromADXL345ViaSpi(XL345_DATAY0,1,SYS_adxl_buf);
  54.             SYS_y_data = SYS_y_data +SYS_adxl_buf[0];


  55.             ReadFromADXL345ViaSpi(XL345_DATAZ1,1,SYS_adxl_buf);
  56.             SYS_z_data = SYS_adxl_buf[0];
  57.             SYS_z_data = SYS_z_data<<8;
  58.             ReadFromADXL345ViaSpi(XL345_DATAZ0,1,SYS_adxl_buf);
  59.             SYS_z_data = SYS_z_data +SYS_adxl_buf[0];

  60.                      
  61.         
  62.         }
  63.    
  64.         /* 3- MODBUS_RTU通信处理 */
  65.         mod_process();
  66.    
  67.         /* x-喂狗 */
  68.         WDT_reset();
  69.     }
  70.     /****** 主循环 *******/
  71.        
  72.                
  73. }

 楼主| 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 | 显示全部楼层











  1. adxl345的spi驱动程序;



  2. /**
  3.   ******************************************************************************
  4.   * [url=home.php?mod=space&uid=288409]@file[/url]    DRV_spi.c
  5.   * [url=home.php?mod=space&uid=187600]@author[/url]  Shine
  6.   * [url=home.php?mod=space&uid=895143]@version[/url] V1.0
  7.   * [url=home.php?mod=space&uid=212281]@date[/url]    2017-06-29
  8.   * @brief   硬件看门狗功能
  9.   ******************************************************************************
  10.   * @attention
  11.   *
  12.   *
  13.   ******************************************************************************
  14.   */
  15. /* 加载官方头文件 */
  16. #include "config.h"

  17. //////////////////////////////////////////////////////////==硬件spi
  18. //----------------------------------------------------------------------------
  19. void SPI_Initial(void)
  20. {      
  21.     //P15_Quasi_Mode;     // P15 (SS) Quasi mode
  22.     P10_Quasi_Mode;     // P10(SPCLK) Quasi mode
  23.     P00_Quasi_Mode;     // P00 (MOSI) Quasi mode
  24.     P01_Quasi_Mode;     // P22 (MISO) Quasi mode
  25.    
  26.     set_DISMODF;        // SS General purpose I/O ( No Mode Fault )
  27.     clr_SSOE;   
  28.    
  29.     clr_LSBFE;          // MSB first         
  30.    
  31.     clr_CPOL;           // The SPI clock is low in idle mode
  32.     set_CPHA;           // The data is sample on the second edge of SPI clock
  33.         
  34.     set_MSTR;           // SPI in Master mode
  35.     SPICLK_DIV16;       // Select SPI clock
  36.     set_SPIEN;          // Enable SPI function
  37.     clr_SPIF;
  38. }


  39. void delay (signed int length)
  40. {
  41.         while (length >0)
  42.             length--;
  43. }

  44. //---------------------------------
  45. //WriteToADXL345ViaSpi();
  46. //---------------------------------
  47. //Function that writes to the ADXL345 via the SPI port. It sends first the control
  48. //word that includes the start address and then the data to write.
  49. //--------------------------------------------------------------------------------
  50. void WriteToADXL345ViaSpi(unsigned char RegisterAddress, unsigned char NumberofRegisters, unsigned char *RegisterData)
  51. {
  52.         unsigned char ControlValue = 0;
  53.         unsigned char ValueToWrite = 0;
  54.         signed char RegisterIndex = 0;
  55.         unsigned char i = 0;

  56.         //Create the 8-bit header
  57.         ControlValue = RegisterAddress;

  58. // SET_SCL();
  59. // delay(1);
  60.         SET_CS();
  61.         SET_SCL();
  62.         delay(3);
  63.         CLR_CS(); //bring CS low
  64.         delay(3);

  65.         CLR_SCL();
  66.         CLR_SDO(); //set WRITE bit
  67.         delay(3);
  68.         SET_SCL();
  69.         delay(3);
  70.         ControlValue <<= 1;

  71.         CLR_SCL();
  72.         SET_SDO(); //set MB bit
  73.         delay(3);
  74.         SET_SCL();
  75.         delay(3);
  76.         ControlValue <<= 1;

  77.         //Write out the control word
  78.         for(i=0; i<6; i++)
  79.         {
  80.         CLR_SCL();
  81.         if(0x80 == (ControlValue & 0x80))
  82.         {
  83.         SET_SDO();   //Send one to SDO pin
  84.         }
  85.         else
  86.         {
  87.         CLR_SDO();   //Send zero to SDO pin
  88.         }
  89.         delay(3);
  90.         SET_SCL();
  91.         delay(3);
  92.         ControlValue <<= 1; //Rotate data
  93.         }

  94.         //And then the data
  95.         for (RegisterIndex=NumberofRegisters; RegisterIndex>0; RegisterIndex--)
  96.         {
  97.         ValueToWrite = *(RegisterData + RegisterIndex - 1);
  98.         for (i=0; i<8; i++)
  99.         {
  100.         CLR_SCL();
  101.         if(0x80 == (ValueToWrite & 0x80))
  102.         {
  103.         SET_SDO();   //Send one to SDO pin
  104.         }
  105.         else
  106.         {
  107.         CLR_SDO();   //Send zero to SDO pin
  108.         }
  109.         delay(3);
  110.         SET_SCL();
  111.         delay(3);
  112.         ValueToWrite <<= 1; //Rotate data
  113.         }
  114.         }
  115.         SET_CS(); //bring CS high again
  116. }

  117. //---------------------------------
  118. //ReadFromADXL345ViaSpi();
  119. //---------------------------------
  120. //Function that reads from the ADXL345 via the SPI port. It first send the control word
  121. //that includes the start address and then 8 clocks for each register to read.
  122. //--------------------------------------------------------------------------------
  123. void ReadFromADXL345ViaSpi(unsigned char RegisterAddress, unsigned char NumberofRegisters, unsigned char *RegisterData)
  124. {
  125.         unsigned char ControlValue = 0;
  126.         signed char RegisterIndex = 0;
  127.         unsigned char ReceiveData = 0;
  128.         unsigned char i = 0;
  129.         unsigned int iTemp = 0;

  130.         //Create the 8-bit header
  131.         ControlValue = RegisterAddress;

  132. // SET_SCL();
  133. // delay(1);
  134.         SET_CS();
  135.         SET_SCL();
  136.         delay(3);
  137.         CLR_CS(); //bring CS low
  138.         delay(3);

  139.         CLR_SCL();
  140.         SET_SDO(); //set READ bit
  141.         delay(3);
  142.         SET_SCL();
  143.         delay(3);
  144.         ControlValue <<= 1;

  145.         CLR_SCL();
  146.         SET_SDO(); //set MB bit
  147.         delay(3);
  148.         SET_SCL();
  149.         delay(3);
  150.         ControlValue <<= 1;

  151.         //Write out the control word
  152.         for(i=0; i<6; i++)
  153.         {
  154.         CLR_SCL();
  155.         if(0x80 == (ControlValue & 0x80))
  156.         {
  157.         SET_SDO();   //Send one to SDO pin
  158.         }
  159.         else
  160.         {
  161.         CLR_SDO();   //Send zero to SDO pin
  162.         }
  163.         delay(3);
  164.         SET_SCL();
  165.         delay(3);
  166.         ControlValue <<= 1; //Rotate data
  167.         }

  168.         //Read data in
  169.         for (RegisterIndex=NumberofRegisters; RegisterIndex>0; RegisterIndex--)
  170.         {
  171.         for(i=0; i<8; i++)
  172.         {
  173.         CLR_SCL();
  174.         ReceiveData <<= 1; //Rotate data
  175.         delay(3);
  176.         SET_SCL();
  177.         //iTemp = GP1DAT; //Read SDI of ADXL345
  178.         if(ADXL345_SDI)    //SDI  P1.5
  179.         {
  180.         ReceiveData |= 1;
  181.         }
  182.         delay(2);
  183.         }
  184.         *(RegisterData + RegisterIndex - 1) = ReceiveData;
  185.         }
  186.         SET_CS(); //bring CS high again
  187. }




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



  189. /**
  190.   ******************************************************************************
  191.   * @file    DRV_dataflash.c
  192.   * @author  Shine
  193.   * @version V1.0
  194.   * @date    2017-06-29
  195.   * @brief   利用flash,模拟eeprom,作为掉电存储,全部设置为APROM,APROM的最后2K空间
  196.   *          (3800-47FFh)
  197.   ******************************************************************************
  198.   * @attention
  199.   *
  200.   *
  201.   ******************************************************************************
  202.   */


  203. /*---------------------------------------------------------------------------------------------------------*/
  204. /*                                                                                                         */
  205. /* Copyright(c) 2017 Nuvoton Technology Corp. All rights reserved.                                         */
  206. /*                                                                                                         */
  207. /*---------------------------------------------------------------------------------------------------------*/

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

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


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

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

  229.         u8_addrl_r = u16_addr;//Shine: 获取要写入地址的低8位,根据是否大于128 完成页面的强制对齐
  230. if (u8_addrl_r<0x80)
  231.         {
  232.         u8_addrl_r = 0;
  233.         }
  234.         else
  235.         {
  236.         u8_addrl_r = 0x80;
  237.         }
  238.    
  239. ///////////Save APROM data to XRAM:
  240.         //xd_tmp = 0x80;        /// 原来写法,直接利用xdata的0x80-0x100来作为缓存有风险,应该用固定内存
  241. xd_tmp = &flashram[0];  /// 改为由系统分配的临时缓存的首地址,当然,存在内存的资源浪费
  242. cd_longaddr = (u16_addr&0xff00)+u8_addrl_r; ///获取要写入的flash地址
  243. //while (xd_tmp !=0x100)
  244.         while (xd_tmp !=(&flashram[0]+0x80) )///从基址开始依次读取128byte
  245.         {
  246.         *xd_tmp = *cd_longaddr;
  247.         looptmp++;
  248.         xd_tmp++;
  249.         cd_longaddr++;
  250.         }
  251.    
  252. /////////Modify customer data in XRAM
  253. /// 官方的库写法,但是个人认为有隐患, 0x80的xram可能会被系统分配;
  254. // u8_addrl_r = u16_addr; ///获取写入地址,分: <128 ;和大于128两种情况
  255. // if (u8_addrl_r<0x80)   ///写入地址<128的,直接从xram的0x80,也就是128开始写入xram
  256. // {
  257. // xd_tmp = u8_addrl_r+0x80;
  258. // }
  259. // else                  ///大于128的,也是从128后的地址开始写入,只是不在需要+128的偏置.
  260. // {
  261. // xd_tmp = u8_addrl_r+0;
  262. // }
  263. // *xd_tmp = u8_data;///地址修正后,直接写入
  264.    
  265. u8_addrl_r = u16_addr;
  266.    
  267.     /* 将写入地址0-256:变成0-128;因为xram混存仅为128*/
  268.         if (u8_addrl_r<0x80)
  269.         {
  270.         xd_tmp =  &flashram[0] + u8_addrl_r;
  271.         }
  272.         else
  273.         {
  274.         xd_tmp =  &flashram[0] +(u8_addrl_r-0x80);
  275.         }
  276.         *xd_tmp = u8_data;
  277.         
  278.    
  279. ////////Erase APROM DATAFLASH page
  280.         IAPAL = u16_addr;   ///获取要写入地址的低位和高位;
  281. IAPAH = u16_addr>>8;///
  282.         IAPFD = 0xFF;
  283.         set_IAPEN;
  284.         set_APUEN;
  285.         IAPCN = 0x22;
  286.         set_IAPGO;
  287.         
  288. /////////Save changed RAM data to APROM DATAFLASH
  289. // u8_addrl_r = u16_addr;
  290. // if (u8_addrl_r<0x80)
  291. // {
  292. // u8_addrl_r =0;
  293. // }
  294. // else
  295. // {
  296. // u8_addrl_r = 0x80;
  297. // }
  298. //        xd_tmp = 0x80;
  299. //        IAPAL = u8_addrl_r;
  300. //        IAPAH = u16_addr>>8;
  301. //        set_IAPEN;
  302. //        set_APUEN;
  303. //        IAPCN = 0x21;
  304. // while (xd_tmp !=0xFF)
  305. // {
  306. // IAPFD = *xd_tmp;
  307. // set_IAPGO;
  308. // IAPAL++;
  309. // xd_tmp++;
  310. // }
  311. //        
  312.     /* 128字节依次写入 flash */
  313.         u8_addrl_r = u16_addr;
  314.         if (u8_addrl_r<0x80)
  315.         {
  316.         u8_addrl_r =0;
  317.         }
  318.         else
  319.         {
  320.         u8_addrl_r = 0x80;
  321.         }
  322.         xd_tmp =   &flashram[0] ;
  323.         IAPAL = u8_addrl_r;
  324.         IAPAH = u16_addr>>8;
  325.         set_IAPEN;
  326.         set_APUEN;
  327.         IAPCN = 0x21;
  328.         while (xd_tmp !=(&flashram[0]+0x80) )
  329.         {
  330.         IAPFD = *xd_tmp;
  331.         set_IAPGO;
  332.         IAPAL++;
  333.         xd_tmp++;
  334.         }
  335.         
  336.      
  337.         clr_APUEN;
  338.         clr_IAPEN;
  339. }

  340. //-------------------------------------------------------------------------
  341. UINT8 read_APROM_BYTE(UINT16 code *u16_addr)
  342. {
  343.         UINT8 rdata;
  344.         rdata = *u16_addr>>8;
  345.         return rdata;
  346. }

  347. /******************************************************************************************************************/



  348. //void main (void)
  349. //{
  350. // UINT8 datatemp;
  351. ///* -------------------------------------------------------------------------*/
  352. ///*  Dataflash use APROM area, please ALWAYS care the address of you code    */
  353. ///* APROM 0x3800~0x38FF demo as dataflash             */
  354. ///* Please use Memory window key in C:0x3800 to check earse result */      
  355. ///* -------------------------------------------------------------------------*/
  356. // InitialUART0_Timer1(115200);
  357. ////call write byte
  358. // write_DATAFLASH_BYTE (0x3881,0x55);
  359. // write_DATAFLASH_BYTE (0x3882,0x56);
  360. // write_DATAFLASH_BYTE (0x3855,0xaa);
  361. // write_DATAFLASH_BYTE (0x3856,0x66);
  362. ////call read byte
  363. // datatemp = read_APROM_BYTE(0x3882);

  364. //    while(1)
  365. // {
  366. //// printf ("\n data temp = 0x%bx", datatemp);
  367. // }
  368. //}



  369. #ifndef _DRV_SPI_H
  370. #define _DRV_SPI_H

  371. void SPI_Initial(void);
  372. void ReadFromADXL345ViaSpi(unsigned char RegisterAddress, unsigned char NumberofRegisters, unsigned char *RegisterData);
  373. void WriteToADXL345ViaSpi(unsigned char RegisterAddress, unsigned char NumberofRegisters, unsigned char *RegisterData);

  374. #define SET_CS() ADXL345_CS =1
  375. #define CLR_CS() ADXL345_CS =0

  376. #define SET_SCL() ADXL345_SCLK =1
  377. #define        CLR_SCL() ADXL345_SCLK =0

  378. #define SET_SDO() ADXL345_SDO = 1
  379. #define CLR_SDO() ADXL345_SDO = 0

  380. #endif
huangcunxiake 发表于 2017-7-4 23:48 | 显示全部楼层
给力,哟图有真相。
 楼主| Harvard 发表于 2017-7-5 11:07 | 显示全部楼层
16MHZ内部晶振 , 通信在115200 ,非常稳定 .内部晶振的精度不错.
yiyigirl2014 发表于 2017-7-18 19:13 | 显示全部楼层
这个价格真是给力。
 楼主| Harvard 发表于 2017-7-19 15:41 | 显示全部楼层
价格不错 片子很好用 .目前就是有个小问题. 在线调试的时候, watch里面的变量不会自动更新. 暂停以后才会变化. 已经勾选了 periodical.... 这个选项.请高手指教.
 楼主| Harvard 发表于 2017-7-20 14:22 | 显示全部楼层
来个实物图;
425133339255381455.jpg

再上个接到上位机的:
0c7307c2a3a854bad1f9d3d1db6bc90.png


yiyigirl2014 发表于 2017-7-20 20:46 | 显示全部楼层
前天我一个朋友开发了个产品,量产了三万,用的就是这个MCU。
lss1985 发表于 2017-8-2 15:59 | 显示全部楼层
DRV_dataflash.c这个在哪个BSP库里?
 楼主| Harvard 发表于 2017-8-5 11:59 | 显示全部楼层
照抄就行 自己写的
wang0225 发表于 2017-8-23 15:24 | 显示全部楼层
能给分源码工程不,楼主
 楼主| Harvard 发表于 2017-8-24 22:28 | 显示全部楼层
可以的
wang0225 发表于 2017-8-25 08:43 | 显示全部楼层
 楼主| Harvard 发表于 2017-9-7 08:25 | 显示全部楼层
源码已经发送 希望对感兴趣的朋友有用.
 楼主| Harvard 发表于 2017-9-7 08:25 | 显示全部楼层
P.S 谁能解决调试003时, 在watch窗口中的变量不能自动更新的问题,必须要暂停调试 才可以看到变化 郁闷的
挖东衣宇 发表于 2017-9-7 11:12 | 显示全部楼层
可以分享一下源码吗,1119029110@qq.com,想学习一下前辈的经验,感谢!
 楼主| Harvard 发表于 2017-9-9 19:20 | 显示全部楼层
mintspring 发表于 2017-9-10 17:06 来自手机 | 显示全部楼层
485确实用的挺多的,传的比232选多了
newphj 发表于 2017-9-13 09:04 | 显示全部楼层
你好,是使用keil C51编译的吗?
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:单片机工作者

74

主题

1732

帖子

21

粉丝
快速回复 在线客服 返回列表 返回顶部