[嵌入式开发模块]MC9S12XEP100 SPI模块 驱动程序

之前发了MC9S12XEP100数据手册上的SPI模块的翻译http://blog.csdn.net/lin_strong/article/details/79122482

于是乎,现在对其的封装也来了。目前先完成了硬件驱动部分的封装。软件部分还没完全想好怎么弄。


这次的封装我尝试使用两个头文件的方式来区分protect与public。
全部内部使用的函数(不带参数检查)都放在了SPI_Internal.h中,而对用户的接口(可选参数检查)都放在了SPI.h中
使用时请只#include “SPI.h” 然后使用里头提供的接口。

下面上代码:

/*
*******************************************************************************************
*
*
*                       SPI(Serial Peripheral Interface) SUPPORT PACKAGE
*                                   Freescale MC9S12XEP100
*                           飞思卡尔   MC9S12XEP100  SPI支持包
*
* File : SPI.h
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2018/02/01
* version: V1.0
* History: 2018/02/01  V1.0   the prototype
* NOTE(s):a.refer to the code of uCOS-II
*         b.this file define the interface for user. user should include this file to use the 
*           spi module.
*         c.this module has three files — SPI.h、SPI_Internal.h、SPI.c
*         d.steps to use this module:
*              1. configure whether you use single port in     CONFIGURE
*              2. configure the init parameter in     INITIALIZATION  CONFIGURE
*              3. use the interface function to send/receive data. e.g.
*                   ......
*                   INT8U dataSend,dataRecv;
*                   SPI_Init(SPI0);
*                   SPI_Enable(SPI0);
*                   for(TRUE){
*                      dataRecv = SPI_ExchangeChar(SPI0,dataSend);
*                      // deal with the dataRecv.
*                   }
*********************************************************************************************
*/

#ifndef   SPI_H
#define   SPI_H

/*
********************************************************************************************
*                                   MISCELLANEOUS
********************************************************************************************
*/

#ifndef  FALSE
#define  FALSE    0
#endif

#ifndef  TRUE
#define  TRUE     1
#endif

#ifndef  NULL
#define  NULL      0x00  
#endif

/*
******************************************************************************************
*                                    CONSTANT
******************************************************************************************
*/

#define   SPI0      0x00                         /*   SPI0           */
#define   SPI1      0x01                         /*   SPI1           */
#define   SPI2      0x02                         /*   SPI2           */


// spi behavior when cpu is in wait mode
#define SPI_INWAITMODE_STOP    0      // SPI module will keep running when cpu in wait mode
#define SPI_INWAITMODE_RUN     1      // SPI module will stop when cpu in wait mode

// format of spi clock
#define SPI_SCKFMT_MODE_0      0      // CPHA=0、CPOL=0
#define SPI_SCKFMT_MODE_1      1      // CPHA=0、CPOL=1
#define SPI_SCKFMT_MODE_2      2      // CPHA=1、CPOL=0
#define SPI_SCKFMT_MODE_3      3      // CPHA=1、CPOL=1

// the bit order when transmit/receive.
#define SPI_BITORDER_LSB       0      // Least significant bit first
#define SPI_BITORDER_MSB       1      // Most significant bit first

// how spi system control SS pin
#define SPI_SS_CTRL_OFF        0      // SPI module don't use SS pin.
#define SPI_SS_CTRL_ON         1      // only for master, SS pin is used for slave control.
#define SPI_SS_LISTEN_MODF     2      // only for master, SS pin is used for listening mode fault 

/*
*******************************************************************************************
*                               CONFIGURE 主配置
*******************************************************************************************
*/

#define  SPI_MODULE_EN            TRUE        /* TRUE: 启用SPI驱动模块        */

// TRUE: just use single port(declare in SPI_MONOPORT_USED), so the module can create
//       optimised code.
#define  SPI_MONOPORT_NEEDED      FALSE        
#define  SPI_MONOPORT_USED        SPI0

#define  SPI_ARGUMENT_CHECK_EN    TRUE        /* TRUE: 启用参数检查        */

/*
*******************************************************************************************
*                                    INCLUDES
*******************************************************************************************
*/

#include <MC9S12XEP100.h>

/*
****************************************************************************************
*                                  ERROR CODES
****************************************************************************************
*/

#define  SPI_NO_ERR            0        /* Function call was successful                       */  
#define  SPI_INVALID_PORT      1        /* Invalid communications port channel                */  
#define  SPI_ARGUMENT_OUT      2        /* Argument out of range                              */  
#define  SPI_RX_EMPTY          3        /* Rx buffer is empty, no character available         */  
#define  SPI_TX_FULL           4        /* Tx buffer is full, could not deposit character     */  
#define  SPI_TX_EMPTY          5        /* If the Tx buffer is empty.                         */  
#define  SPI_RX_TIMEOUT        6        /* If a timeout occurred while waiting for a character*/  
#define  SPI_TX_TIMEOUT        7        /* If a timeout occurred while waiting to send a char.*/
#define  SPI_ERR_UNKNOWN       8

/*
******************************************************************************************
*                                  TYPE DEFINE
******************************************************************************************
*/
// for any init-style
typedef struct spi_init_struct{
  unsigned char isMaster;   // TRUE: spi is master. FALSE: spi is slave.
  unsigned char SSctrl;     // see SPI_SS_CTRL
  unsigned char bitorder;   // see SPI_BITORDER
  unsigned char inWaitMode; // see SPI_INWAITMODE
  unsigned char sckfmt;     // see SPI_SCKFMT
// BaudRateDivisor = (SPPR + 1) * 2^(SPR + 1)
// BaudRate = Busclock / BaudRateDivisor
  unsigned char SPR;        // 0-7,SPI Baud Rate Selection
  unsigned char SPPR;       // 0-7,SPI Baud Rate Preselection
}SPI_INIT_STRUCT,*pSPI_INIT_STRUCT;

/*
*******************************************************************************************
*                        INITIALIZATION  CONFIGURE 初始化配置
*******************************************************************************************
*/

// the default parameter to initialize the SPI system
// 初始化SPI的默认参数
#define  SPI_INIT_ISMASTER         TRUE                 // TRUE: spi is master. FALSE: spi is slave.
#define  SPI_INIT_SSCTRL           SPI_SS_CTRL_ON       // see SPI_SS_CTRL
#define  SPI_INIT_BITORDER         SPI_BITORDER_MSB     // see SPI_BITORDER
#define  SPI_INIT_INWAITMODE       SPI_INWAITMODE_STOP  // see SPI_INWAITMODE
#define  SPI_INIT_SCKFMT           SPI_SCKFMT_MODE_3    // see SPI_SCKFMT
// BaudRateDivisor = (SPPR + 1) * 2^(SPR + 1)
// BaudRate = Busclock / BaudRateDivisor
#define  SPI_INIT_SPR              1                    // 0-7,SPI Baud Rate Selection
#define  SPI_INIT_SPPR             3                    // 0-7,SPI Baud Rate Preselection

/*
************************************************************************************
*                          FUNCTION PROTOTYPES  函数原型
************************************************************************************
*/

// unsigned char SPI_Init(unsigned char port,pSPI_INIT_STRUCT arg); 
unsigned char SPI_Init(unsigned char port);

unsigned char SPI_Enable(unsigned char port);
unsigned char SPI_Disable(unsigned char port);
unsigned char SPI_EnableTxInt(unsigned char port);
unsigned char SPI_DisableRxInt(unsigned char port);
unsigned char SPI_EnableRxInt(unsigned char port);  
unsigned char SPI_DisableTxInt(unsigned char port);

unsigned char SPI_setMaster(unsigned char port,unsigned char isMaster);
unsigned char SPI_setSSctrl(unsigned char port,unsigned char option);
unsigned char SPI_setBehaviorInWaitMode(unsigned char port,unsigned char option);
unsigned char SPI_setSCKFormat(unsigned char port,unsigned char option);
unsigned char SPI_setBaudRate(unsigned char port,unsigned char SPR,unsigned char SPPR);

unsigned char SPI_PutChar(unsigned char port, unsigned char c);
unsigned char SPI_DataHasReceived(unsigned char port);
unsigned char SPI_GetChar(unsigned char port);  
unsigned char SPI_ExchangeChar(unsigned char port, unsigned char c);  

/*
************************************************************************************
*                          ERROR CHECK 错误检查
************************************************************************************
*/

#ifndef SPI_MODULE_EN
   #error "SPI_MODULE_EN must be defined."
#endif

#if (SPI_MONOPORT_NEEDED == TRUE)
  #ifndef SPI_MONOPORT_USED
     #error "SPI_MONOPORT_USED must be specified if you use single port."
  #endif
#endif

#if (SPI_INIT_BITORDER != SPI_BITORDER_LSB && SPI_INIT_BITORDER != SPI_BITORDER_MSB)
  #error "SPI_INIT_BITORDER must be SPI_BITORDER_LSB or SPI_BITORDER_MSB"
#endif

#if(SPI_INIT_ISMASTER != TRUE && SPI_INIT_ISMASTER != FALSE)
  #error "SPI_INIT_ISMASTER must be TRUE or FALSE"
#endif

#if(SPI_INIT_SSCTRL != SPI_SS_CTRL_ON && SPI_INIT_SSCTRL != SPI_SS_CTRL_OFF && \
    SPI_INIT_SSCTRL != SPI_SS_LISTEN_MODF)
  #error "SPI_INIT_SSCTRL must be SPI_SS_CTRL_ON or SPI_SS_CTRL_OFF or SPI_SS_LISTEN_MODF"
#endif

#if(SPI_INIT_BITORDER != SPI_BITORDER_MSB && SPI_INIT_BITORDER != SPI_BITORDER_LSB)
  #error "SPI_INIT_BITORDER must be SPI_BITORDER_MSB or SPI_BITORDER_LSB"
#endif

#if(SPI_INIT_INWAITMODE != SPI_INWAITMODE_STOP && SPI_INIT_INWAITMODE != SPI_INWAITMODE_RUN)
  #error "SPI_INIT_INWAITMODE must be SPI_INWAITMODE_STOP or SPI_INWAITMODE_RUN"
#endif

#if(SPI_INIT_SCKFMT != SPI_SCKFMT_MODE_0 && SPI_INIT_SCKFMT != SPI_SCKFMT_MODE_1 && \
    SPI_INIT_SCKFMT != SPI_SCKFMT_MODE_2 && SPI_INIT_SCKFMT != SPI_SCKFMT_MODE_3)
  #error "SPI_INIT_SSCTRL must be SPI_SCKFMT_MODE_X(X=0/1/2/3)"
#endif

#if(SPI_INIT_SPR < 0 || SPI_INIT_SPR > 7 ||SPI_INIT_SPPR < 0 || SPI_INIT_SPPR > 7 )
  #error "SPI_INIT_SPR and SPI_INIT_SPPR must between 0 and 7"
#endif

#endif  // of  SPI_DEF_H
/*
*******************************************************************************************
*
*
*                       SPI(Serial Peripheral Interface) SUPPORT PACKAGE
*                                   Freescale MC9S12XEP100
*                           飞思卡尔   MC9S12XEP100  SPI支持包
*
* File : SPI_Internal.h
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2018/02/01
* version: V1.0
* History: 2018/02/01  V1.0   the prototype
* NOTE(s):a.refer to the code of uCOS-II
*         b.this file is for internal use or extension. User shouldn't include this file.
*           这个文件是用于内部使用或扩展的。用户不应该include这个文件
*********************************************************************************************
*/

#ifndef   SPI_INTERNAL_H
#define   SPI_INTERNAL_H

/*
*******************************************************************************************
*                                    INCLUDES
*******************************************************************************************
*/

#include "SPI.h"

#if(SPI_MONOPORT_NEEDED == TRUE)
  #define  SPI_MONO  SPI_MONOPORT_USED
#endif

/*
******************************************************************************************
*                                  TYPE DEFINITION
******************************************************************************************
*/

// struct of SPI register
typedef struct{
  SPI0CR1STR CR1;
  SPI0CR2STR CR2;
  SPI0BRSTR  BR;
  SPI0SRSTR  SR;
  SPI0DRSTR  DR;
} SPISTR,*pSPISTR;

// 如定义了SPI_MONO则会为单个端口生成代码 
#ifdef SPI_MONO
  #if(SPI_MONO == 0)
    #define SPICR1(pSPIstr)    _SPI0CR1
    #define SPICR2(pSPIstr)    _SPI0CR2
    #define SPIBR(pSPIstr)     _SPI0BR
    #define SPISR(pSPIstr)     _SPI0SR
    #define SPIDR(pSPIstr)     SPI0DR
    #define SPIDRL(pSPIstr)    SPI0DRL
    #define SPIDRH(pSPIstr)    SPI0DRH
  #elif(SPI_MONO == 1)
    #define SPICR1(pSPIstr)    _SPI1CR1
    #define SPICR2(pSPIstr)    _SPI1CR2
    #define SPIBR(pSPIstr)     _SPI1BR
    #define SPISR(pSPIstr)     _SPI1SR
    #define SPIDR(pSPIstr)     SPI1DR
    #define SPIDRL(pSPIstr)    SPI1DRL
    #define SPIDRH(pSPIstr)    SPI1DRH
  #elif(SPI_MONO == 2)
    #define SPICR1(pSPIstr)    _SPI2CR1
    #define SPICR2(pSPIstr)    _SPI2CR2
    #define SPIBR(pSPIstr)     _SPI2BR
    #define SPISR(pSPIstr)     _SPI2SR
    #define SPIDR(pSPIstr)     SPI2DR
    #define SPIDRL(pSPIstr)    SPI2DRL
    #define SPIDRH(pSPIstr)    SPI2DRH
  #else
    #error "SPI_MONO must be 0(for SPI0)/1(for SPI1)/2(for SPI2)."
  #endif 
#else
  #define SPICR1(pSPIstr)        (pSPIstr->CR1)
  #define SPICR2(pSPIstr)        (pSPIstr->CR2)
  #define SPIBR(pSPIstr)         (pSPIstr->BR)
  #define SPISR(pSPIstr)         (pSPIstr->SR)
  #define SPIDR(pSPIstr)         (pSPIstr->DR.Word)
  #define SPIDRL(pSPIstr)        (pSPIstr->DR.Overlap_STR.SPI0DRLSTR.Byte)
  #define SPIDRH(pSPIstr)        (pSPIstr->DR.Overlap_STR.SPI0DRHSTR.Byte)
#endif

/*** SPICR1 - SPI Control Register 1 ***/

#define SPICR1_BYTE(pSPIstr)        (SPICR1(pSPIstr).Byte)
#define SPICR1_BITS(pSPIstr)        (SPICR1(pSPIstr).Bits)
#define SPICR1_LSBFE(pSPIstr)       (SPICR1_BITS(pSPIstr).LSBFE)   /* SPI LSB-First Enable */
#define SPICR1_SSOE(pSPIstr)        (SPICR1_BITS(pSPIstr).SSOE)    /* Slave Select Output Enable */
#define SPICR1_CPHA(pSPIstr)        (SPICR1_BITS(pSPIstr).CPHA)    /* SPI Clock Phase Bit */
#define SPICR1_CPOL(pSPIstr)        (SPICR1_BITS(pSPIstr).CPOL)    /* SPI Clock Polarity Bit */
#define SPICR1_MSTR(pSPIstr)        (SPICR1_BITS(pSPIstr).MSTR)    /* SPI Master/Slave Mode Select Bit */
#define SPICR1_SPTIE(pSPIstr)       (SPICR1_BITS(pSPIstr).SPTIE)   /* SPI Transmit Interrupt Enable */
#define SPICR1_SPE(pSPIstr)         (SPICR1_BITS(pSPIstr).SPE)     /* SPI System Enable Bit */
#define SPICR1_SPIE(pSPIstr)        (SPICR1_BITS(pSPIstr).SPIE)    /* SPI Interrupt Enable Bit */

#define SPICR1_LSBFE_MASK              1U
#define SPICR1_SSOE_MASK               2U
#define SPICR1_CPHA_MASK               4U
#define SPICR1_CPOL_MASK               8U
#define SPICR1_MSTR_MASK               16U
#define SPICR1_SPTIE_MASK              32U
#define SPICR1_SPE_MASK                64U
#define SPICR1_SPIE_MASK               128U

/*** SPICR2 - SPI Control Register 2 ***/

#define SPICR2_BYTE(pSPIstr)        (SPICR2(pSPIstr).Byte)
#define SPICR2_BITS(pSPIstr)        (SPICR2(pSPIstr).Bits)
#define SPICR2_SPC0(pSPIstr)        (SPICR2_BITS(pSPIstr).SPC0)
#define SPICR2_SPISWAI(pSPIstr)     (SPICR2_BITS(pSPIstr).SPISWAI)
#define SPICR2_BIDIROE(pSPIstr)     (SPICR2_BITS(pSPIstr).BIDIROE)
#define SPICR2_MODFEN(pSPIstr)      (SPICR2_BITS(pSPIstr).MODFEN)
#define SPICR2_XFRW(pSPIstr)        (SPICR2_BITS(pSPIstr).XFRW)

#define SPICR2_SPC0_MASK               1U
#define SPICR2_SPISWAI_MASK            2U
#define SPICR2_BIDIROE_MASK            8U
#define SPICR2_MODFEN_MASK             16U
#define SPICR2_XFRW_MASK               64U

/*** SPIBR - SPI Baud Rate Register ***/

#define SPIBR_BYTE(pSPIstr)         (SPIBR(pSPIstr).Byte)
#define SPIBR_BITS(pSPIstr)         (SPIBR(pSPIstr).Bits)
#define SPIBR_MBITS(pSPIstr)        (SPIBR(pSPIstr).MergedBits)
#define SPIBR_SPR0(pSPIstr)         (SPIBR_BITS(pSPIstr).SPR0)       /* SPI Baud Rate Selection Bit 0 */
#define SPIBR_SPR1(pSPIstr)         (SPIBR_BITS(pSPIstr).SPR1)       /* SPI Baud Rate Selection Bit 1 */
#define SPIBR_SPR2(pSPIstr)         (SPIBR_BITS(pSPIstr).SPR2)       /* SPI Baud Rate Selection Bit 2 */
#define SPIBR_SPPR0(pSPIstr)        (SPIBR_BITS(pSPIstr).SPPR0)      /* SPI Baud Rate Preselection Bits 0 */
#define SPIBR_SPPR1(pSPIstr)        (SPIBR_BITS(pSPIstr).SPPR1)      /* SPI Baud Rate Preselection Bits 1 */
#define SPIBR_SPPR2(pSPIstr)        (SPIBR_BITS(pSPIstr).SPPR2)      /* SPI Baud Rate Preselection Bits 2 */
#define SPIBR_SPR(pSPIstr)          (SPIBR_MBITS(pSPIstr).grpSPR)
#define SPIBR_SPPR(pSPIstr)         (SPIBR_MBITS(pSPIstr).grpSPPR)

#define SPIBR_SPR0_MASK                1U
#define SPIBR_SPR1_MASK                2U
#define SPIBR_SPR2_MASK                4U
#define SPIBR_SPPR0_MASK               16U
#define SPIBR_SPPR1_MASK               32U
#define SPIBR_SPPR2_MASK               64U
#define SPIBR_SPR_MASK                 7U

/*** SPISR - SPI Status Register ***/

#define SPISR_BYTE(pSPIstr)         (SPISR(pSPIstr).Byte)
#define SPISR_BITS(pSPIstr)         (SPISR(pSPIstr).Bits)
#define SPISR_MODF(pSPIstr)         (SPISR_BITS(pSPIstr).MODF)
#define SPISR_SPTEF(pSPIstr)        (SPISR_BITS(pSPIstr).SPTEF)
#define SPISR_SPIF(pSPIstr)         (SPISR_BITS(pSPIstr).SPIF)

#define SPISR_MODF_MASK                16U
#define SPISR_SPTEF_MASK               32U
#define SPISR_SPIF_MASK                128U

/*** SPIDR - SPI Data Register ***/

#define SPIDR_WORD(pSPIstr)         SPIDR(pSPIstr)
#define SPIDRL_BYTE(pSPIstr)        SPIDRL(pSPIstr)
#define SPIDRH_BYTE(pSPIstr)        SPIDRH(pSPIstr)

/*
******************************************************************************************
*                          REGISTER VARIABLE DECLARATION
******************************************************************************************
*/
extern pSPISTR const ptr_SPI[];
#define  SPI_CNT     3
#define  pSPI(port)  (ptr_SPI[port])

/*
****************************************************************************************
*                                  ERROR CODES
****************************************************************************************
*/

#define  SPI_NO_ERR            0        /* Function call was successful                       */  
#define  SPI_BAD_CH            1        /* Invalid communications port channel                */
#define  SPI_ARGUMENT_OUT      2        /* Argument out of range                              */  
#define  SPI_RX_EMPTY          3        /* Rx buffer is empty, no character available         */  
#define  SPI_TX_FULL           4        /* Tx buffer is full, could not deposit character     */  
#define  SPI_TX_EMPTY          5        /* If the Tx buffer is empty.                         */  
#define  SPI_RX_TIMEOUT        6        /* If a timeout occurred while waiting for a character*/  
#define  SPI_TX_TIMEOUT        7        /* If a timeout occurred while waiting to send a char.*/  
#define  SPI_ERR_UNKNOWN       8

/*
************************************************************************************
*                          FUNCTION PROTOTYPES  函数原型
************************************************************************************
*/

#define SPI_isSystemEnable_set_internal(pSPIstr,b)    (SPICR1_SPE(pSPIstr) = (b))
#define SPI_isSystemEnable_get_internal(pSPIstr)       SPICR1_SPE(pSPIstr)
#define SPI_Enable_internal(pSPIstr)        SPI_isSystemEnable_set_internal(pSPIstr,TRUE)
#define SPI_Disable_internal(pSPIstr)       SPI_isSystemEnable_set_internal(pSPIstr,FALSE)

#define SPI_isMaster_set_internal(pSPIstr,b)          (SPICR1_MSTR(pSPIstr) = (b))
#define SPI_isMaster_get_internal(pSPIstr)             SPICR1_MSTR(pSPIstr)

#define SPI_isStopInWait_set_internal(pSPIstr,b)      (SPICR2_SPISWAI(pSPIstr) = (b))
#define SPI_isStopInWait_get_internal(pSPIstr)         SPICR2_SPISWAI(pSPIstr)

// TRUE: data is word-wide;FALSE:char-wide.
#define SPI_isWordWide_set_internal(pSPIstr,b)        (SPICR2_XFRW(pSPIstr) = (b))
#define SPI_isWordWide_get_internal(pSPIstr)           SPICR2_XFRW(pSPIstr)
#define SPI_isLSBFirst_set_internal(pSPIstr,b)        (SPICR1_LSBFE(pSPIstr) = (b))
#define SPI_isLSBFirst_get_internal(pSPIstr)           SPICR1_LSBFE(pSPIstr)
#define SPI_isHighIdle_set_internal(pSPIstr,b)        (SPICR1_CPHA(pSPIstr) = (b))
#define SPI_isHighIdle_get_internal(pSPIstr)           SPICR1_CPHA(pSPIstr)
#define SPI_isEvenSample_set_internal(pSPIstr,b)      (SPICR1_CPOL(pSPIstr) = (b))
#define SPI_isEvenSample_get_internal(pSPIstr)         SPICR1_CPOL(pSPIstr)

#define SPI_isTxIntEnable_set_internal(pSPIstr,b)     (SPICR1_SPTIE(pSPIstr) = (b))
#define SPI_isTxIntEnable_get_internal(pSPIstr)        SPICR1_SPTIE(pSPIstr)
#define SPI_EnableTxInt_internal(pSPIstr)    SPI_isTxIntEnable_set_internal(pSPIstr,TRUE)
#define SPI_DisableTxInt_internal(pSPIstr)   SPI_isTxIntEnable_set_internal(pSPIstr,FALSE)
// 实际上同时使能了接收和MODF中断
#define SPI_isRxIntEnable_set_internal(pSPIstr,b)     (SPICR1_SPIE(pSPIstr) = (b))
#define SPI_isRxIntEnable_get_internal(pSPIstr)        SPICR1_SPIE(pSPIstr)
#define SPI_EnableRxInt_internal(pSPIstr)    SPI_isRxIntEnable_set_internal(pSPIstr,TRUE)
#define SPI_DisableRxInt_internal(pSPIstr)   SPI_isRxIntEnable_set_internal(pSPIstr,FALSE)

// if is master node. 
// when MODFEN = 1 && SSOE = 0, SS is for dectect MODF;
// when MODFEN = 1 && SSOE = 1, SS is for slave selection;
#define SPI_isSSoutputEnable_set_internal(pSPIstr,b)  (SPICR1_SSOE(pSPIstr) = (b))
#define SPI_isSSoutputEnable_get_internal(pSPIstr)     SPICR1_SSOE(pSPIstr)
#define SPI_isMODFEnable_set_internal(pSPIstr,b)      (SPICR2_MODFEN(pSPIstr) = (b))
#define SPI_isMODFEnable_get_internal(pSPIstr)         SPICR2_MODFEN(pSPIstr)

// BaudRateDivisor = (SPPR + 1) * 2^(SPR + 1)
// BaudRate = Busclock / BaudRateDivisor
// 分频系数 = (SPPR + 1) * 2^(SPR + 1)
// 波特率 = 总线时钟 / 分频系数
#define SPI_SPR_set_internal(pSPIstr,val)             (SPIBR_SPR(pSPIstr) = (val))
#define SPI_SPR_get_internal(pSPIstr)                  SPIBR_SPR(pSPIstr)
#define SPI_SPPR_set_internal(pSPIstr,val)            (SPIBR_SPPR(pSPIstr) = (val))
#define SPI_SPPR_get_internal(pSPIstr)                 SPIBR_SPPR(pSPIstr)

#define SPI_isDataReceived(pSPIstr)                    SPISR_SPIF(pSPIstr)
#define SPI_waitUntilDataReceived(pSPIstr)             while(!SPI_isDataReceived(pSPIstr));
#define SPI_isTxReady(pSPIstr)                         SPISR_SPTEF(pSPIstr)
#define SPI_waitUntilTxReady(pSPIstr)                  while(!SPI_isTxReady(pSPIstr));
#define SPI_isModeFaultHappened(pSPIstr)               SPISR_MODF(pSPIstr)

#define SPI_getChar_internal(pSPIstr)                  SPIDRL_BYTE(pSPIstr)
#define SPI_putChar_internal(pSPIstr,c)                (SPIDRL_BYTE(pSPIstr) = (c))

#define SPI_getWord_internal(pSPIstr)                  SPIDR_WORD(pSPIstr)
#define SPI_putWord_internal(pSPIstr,w)                (SPIDR_WORD(pSPIstr) = (w))

#endif  // of  SPI_DEF_H
/*
*********************************************************************************************************
*
*
*                    SPI(Serial Communication Interface) SUPPORT PACKAGE
*                                         Freescale MC9S12XEP100
*                               飞思卡尔   MC9S12XEP100  SPI支持包
*
* File : SPI.c
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date : 2018/02/01
* version: V1.0
* History: 2018/02/01  V1.0   the prototype
* NOTE(s): 1. refer to the code of uCOS-II
*          2. the function in this file is not thread-safe, which means user should make sure that only 
*             one thread will call the same function for the same port at the same time.
*             这个文件中的实现都不是线程安全的,也就是说,用户需要保证在同一时间只有一个线程调用某一端口
*             的同个函数
*********************************************************************************************************
*/

/*
*********************************************************************************************************
*                                       INCLUDES
*********************************************************************************************************
*/

#include "SPI_Internal.h"
/*
*********************************************************************************************************
*                                REGISTER VARIABLE DEFINITION
*********************************************************************************************************
*/

pSPISTR const ptr_SPI[] = {(pSPISTR)&_SPI0CR1,(pSPISTR)&_SPI1CR1,(pSPISTR)&_SPI2CR1};

/*
*********************************************************************************************************
*                                LOCAL FUNCTION DECLARATION
*********************************************************************************************************
*/

#if(SPI_ARGUMENT_CHECK_EN == TRUE)
  #define argCheck(arg,ceil,rVal)     if((arg) >= (ceil)) return (rVal);
#else
  #define argCheck(arg,ceil,rVal)
#endif // of (SPI_ARGUMENT_CHECK_EN == TRUE)

#if(SPI_MONOPORT_NEEDED == TRUE)
  #define portCheck()
  #define portCheck2(rVal)
#else
  #define portCheck()                    argCheck(port,SPI_CNT,SPI_BAD_CH)
  #define portCheck2(rVal)               argCheck(port,SPI_CNT,rVal)
#endif

#if(SPI_MODULE_EN == TRUE)

/*
*********************************************************************************************************
*                                        SPI_Init()
*
* Description : Initialize SPI support hardware(struct style).  初始化SPI硬件(结构体风格)
*
* Arguments   : port    SPI0-SPI2 the port to Initialize;           选择要初始化的端口;
*               arg     arguments used to initialize the SPI port;  用于初始化SPI端口的参数
*
* Return      : SPI_NO_ERR       if success.
*               SPI_BAD_CH       if invalid port used.
*
* Note(s)     : 1.will use the arguments in init_struct to initialize SPI;
*                 会使用结构体中的参数来初始化SPI
*               2.don't forget to enable the spi after initialization.
*                 不要忘了在初始化后使能SPI
*               3.if you want this style, comment the another SPI_Init() and uncomment this one.
*                 However, the trade-off is that the code size will be much bigger.
*                 如果想要使用这种初始化风格的话,注释掉另一个SPI_Init()然后取消注释这一个。
*                 但是代价就是生成的代码会大得多。
*********************************************************************************************************
*/
/*unsigned char SPI_Init(unsigned char port,pSPI_INIT_STRUCT arg){
  pSPISTR pPort = pSPI(port);
  portCheck();
  argCheck(arg->SPR,8,SPI_ARGUMENT_OUT);
  argCheck(arg->SPPR,8,SPI_ARGUMENT_OUT);
  argCheck(arg->sckfmt,SPI_SCKFMT_MODE_3 + 1,SPI_ARGUMENT_OUT);
  argCheck(arg->SSctrl,SPI_SS_LISTEN_MODF + 1,SPI_ARGUMENT_OUT);
  SPI_isMaster_set_internal(pPort,!!arg->isMaster);
  switch(arg->SSctrl){
    case SPI_SS_CTRL_OFF:
      SPI_isMODFEnable_set_internal(pPort,FALSE);
    break;
    case SPI_SS_CTRL_ON:
      SPI_isMODFEnable_set_internal(pPort,TRUE);
      SPI_isSSoutputEnable_set_internal(pPort,TRUE);
    break;
    case SPI_SS_LISTEN_MODF:
      SPI_isMODFEnable_set_internal(pPort,TRUE);
      SPI_isSSoutputEnable_set_internal(pPort,FALSE);
    break;
  }
  SPI_isHighIdle_set_internal(pPort,!!((arg->sckfmt) & 0x02));
  SPI_isEvenSample_set_internal(pPort,(arg->sckfmt) & 0x01);
  SPI_isLSBFirst_set_internal(pPort, (arg->bitorder) == SPI_BITORDER_LSB);
  SPI_isStopInWait_set_internal(pPort, (arg->inWaitMode) == SPI_INWAITMODE_STOP);
  SPI_SPR_set_internal(pPort,(arg->SPR));
  SPI_SPPR_set_internal(pPort,(arg->SPPR));
  return SPI_NO_ERR;
}  // */
/*
*********************************************************************************************************
*                                        SPI_Init()
*
* Description : Initialize SPI support hardware(marco style).  初始化SPI硬件(宏参数风格)
*
* Arguments   : port    SPI0-SPI2 the port to Initialize;           选择要初始化的端口;
*
* Return      : SPI_NO_ERR       if success.
*               SPI_BAD_CH       if invalid port used.
*
* Note(s)     : 1.will use marco arguments(see SPI_INIT_XXXX) in SPI.h to initialize SPI;
*                 会使用SPI.h 中的宏参数(见 SPI_INIT_XXXX)来初始化SPI
*               2.don't forget to enable the spi after initialization.
*                 不要忘了在初始化后使能SPI
*********************************************************************************************************
*/
unsigned char SPI_Init(unsigned char port){
  pSPISTR pPort = pSPI(port);
  portCheck();
  SPI_isMaster_set_internal(pPort,SPI_INIT_ISMASTER);
#if(SPI_INIT_ISMASTER == TRUE)
  #if(SPI_INIT_SSCTRL == SPI_SS_CTRL_OFF)
    SPI_isMODFEnable_set_internal(pPort,FALSE);
  #elif(SPI_INIT_SSCTRL == SPI_SS_CTRL_ON)
    SPI_isMODFEnable_set_internal(pPort,TRUE);
    SPI_isSSoutputEnable_set_internal(pPort,TRUE);
  #else
    SPI_isMODFEnable_set_internal(pPort,TRUE);
    SPI_isSSoutputEnable_set_internal(pPort,FALSE);
  #endif
#endif
  SPI_isHighIdle_set_internal(pPort,!!(SPI_INIT_SCKFMT & 0x02));
  SPI_isEvenSample_set_internal(pPort,SPI_INIT_SCKFMT & 0x01);
  SPI_isLSBFirst_set_internal(pPort, SPI_INIT_BITORDER == SPI_BITORDER_LSB);
  SPI_isStopInWait_set_internal(pPort, SPI_INIT_INWAITMODE == SPI_INWAITMODE_STOP);
  SPI_SPR_set_internal(pPort,SPI_INIT_SPR);
  SPI_SPPR_set_internal(pPort,SPI_INIT_SPPR);
  return SPI_NO_ERR;
}
/*
*********************************************************************************************************
*                                        SPI_Enable()
*
* Description : Enable SPI System    使能SPI系统
*
* Arguments   : port             SPI0-SPI2 the port to Choose;       选择端口;
*
* Return      : SPI_NO_ERR       if success.
*               SPI_BAD_CH       if invalid port used.
*
* Note: 
*********************************************************************************************************
*/

unsigned char SPI_Enable(unsigned char port){
  portCheck();
  SPI_Enable_internal(pSPI(port));
  return SPI_NO_ERR;
}
/*
*********************************************************************************************************
*                                        SPI_Disable()
*
* Description : Disable SPI System    禁用SPI系统
*
* Arguments   : port               SPI0-SPI2 the port to Choose;       选择端口;
*
* Return      : SPI_NO_ERR       if success.
*               SPI_BAD_CH       if invalid port used.
*
* Note: 
*********************************************************************************************************
*/
unsigned char SPI_Disable(unsigned char port){
  portCheck();
  SPI_Disable_internal(pSPI(port));
  return SPI_NO_ERR;
}

/*
*********************************************************************************************************
*                                        SPI_EnableTxInt()
*
* Description : Enable Tx interrupt   使能传输中断
*
* Arguments   : port               SPI0-SPI2 the port to Choose;       选择端口;
*
* Return      : SPI_NO_ERR       if success.
*               SPI_BAD_CH       if invalid port used.
*
* Note: 
*********************************************************************************************************
*/
unsigned char SPI_EnableTxInt(unsigned char port){
  portCheck();
  SPI_EnableTxInt_internal(pSPI(port));
  return SPI_NO_ERR;
}
/*
*********************************************************************************************************
*                                        SPI_DisableTxInt()
*
* Description : Disable  Tx interrupt  禁用传输中断
*
* Arguments   : port               SPI0-SPI2 the port to Choose;       选择端口;
*
* Return      : SPI_NO_ERR       if success.
*               SPI_BAD_CH       if invalid port used.
*
* Note: 
*********************************************************************************************************
*/
unsigned char SPI_DisableTxInt(unsigned char port){
  portCheck();
  SPI_DisableTxInt_internal(pSPI(port));
  return SPI_NO_ERR;
}

/*
*********************************************************************************************************
*                                        SPI_EnableRxInt()
*
* Description : Enable Rx interrupt   使能接收中断
*
* Arguments   : port             SPI0-SPI2 the port to Choose;       选择端口;
*
* Return      : SPI_NO_ERR       if success.
*               SPI_BAD_CH       if invalid port used.
*
* Note: 
*********************************************************************************************************
*/

unsigned char SPI_EnableRxInt(unsigned char port){
  portCheck();
  SPI_EnableRxInt_internal(pSPI(port));
  return SPI_NO_ERR;
}
/*
*********************************************************************************************************
*                                        SPI_DisableRxInt()
*
* Description : Disable Rx interrupt     禁用接收中断
*
* Arguments   : port               SPI0-SPI2 the port to Choose;       选择端口;
*
* Return      : SPI_NO_ERR       if success.
*               SPI_BAD_CH       if invalid port used.
*
* Note: 
*********************************************************************************************************
*/
unsigned char SPI_DisableRxInt(unsigned char port){
  portCheck();
  SPI_DisableRxInt_internal(pSPI(port));
  return SPI_NO_ERR;
}
/*
*********************************************************************************************************
*                                        SPI_setMaster()
*
* Description : determine whether the SPI  is master(or slave). 设置SPI系统为主机还是从机
*
* Arguments   : port             SPI0-SPI2 the port to Choose;       选择端口;
*               isMaster         TRUE   set SPI port as master
*                                FALSE  set SPI port as slave.
*
* Return      : SPI_NO_ERR       if success.
*               SPI_BAD_CH       if invalid port used.
*
* Note: 
*********************************************************************************************************
*/
unsigned char SPI_setMaster(unsigned char port,unsigned char isMaster){
  portCheck();
  SPI_isMaster_set_internal(pSPI(port),!!isMaster);
  return SPI_NO_ERR;
}
/*
*********************************************************************************************************
*                                        SPI_setSSctrl()
*
* Description : determine the function of SS pin. 设置SS引脚的功能
*
* Arguments   : port             SPI0-SPI2 the port to Choose;       选择端口;
*               option           see SPI_SS_CTRL
*
* Return      : SPI_NO_ERR       if success.
*               SPI_BAD_CH       if invalid port used.
*               SPI_ARGUMENT_OUT if argument out of range.
* Note: 
*********************************************************************************************************
*/
unsigned char SPI_setSSctrl(unsigned char port,unsigned char option){
  pSPISTR pPort = pSPI(port);
  portCheck();
  argCheck(option,SPI_SS_LISTEN_MODF + 1,SPI_ARGUMENT_OUT);
  switch(option){
    case SPI_SS_CTRL_OFF:
      SPI_isMODFEnable_set_internal(pPort,FALSE);
    break;
    case SPI_SS_CTRL_ON:
      SPI_isMODFEnable_set_internal(pPort,TRUE);
      SPI_isSSoutputEnable_set_internal(pPort,TRUE);
    break;
    case SPI_SS_LISTEN_MODF:
      SPI_isMODFEnable_set_internal(pPort,TRUE);
      SPI_isSSoutputEnable_set_internal(pPort,FALSE);
    break;
  }
  return SPI_NO_ERR;
}
/*
*********************************************************************************************************
*                                SPI_setBehaviorInWaitMode()
*
* Description : determine what SPI system will do when cpu is in wait mode. 设置SPI在CPU为wait模式时的行为
*
* Arguments   : port             SPI0-SPI2 the port to Choose;       选择端口;
*               option           see SPI_INWAITMODE
*
* Return      : SPI_NO_ERR       if success.
*               SPI_BAD_CH       if invalid port used.
*               SPI_ARGUMENT_OUT if argument out of range.
* Note: 
*********************************************************************************************************
*/
unsigned char SPI_setBehaviorInWaitMode(unsigned char port,unsigned char option){
  portCheck();
  SPI_isStopInWait_set_internal(pSPI(port), option == SPI_INWAITMODE_STOP);
  return SPI_NO_ERR;
}
/*
*********************************************************************************************************
*                                        SPI_setSCKFormat()
*
* Description : determine the format of SPI clock. 设置SPI时钟格式
*
* Arguments   : port             SPI0-SPI2 the port to Choose;       选择端口;
*               option           see SPI_SCKFMT
*
* Return      : SPI_NO_ERR       if success.
*               SPI_BAD_CH       if invalid port used.
*               SPI_ARGUMENT_OUT if argument out of range.
* Note: 
*********************************************************************************************************
*/

unsigned char SPI_setSCKFormat(unsigned char port,unsigned char option){
  pSPISTR pPort = pSPI(port);
  portCheck();
  argCheck(option,SPI_SCKFMT_MODE_3 + 1,SPI_ARGUMENT_OUT);
  SPI_isHighIdle_set_internal(pPort,!!(option & 0x02));
  SPI_isEvenSample_set_internal(pPort,option & 0x01);
  return SPI_NO_ERR;
}

/*
*********************************************************************************************************
*                                        SPI_setBaudRate()
*
* Description : determine the baud rate of SPI. 设置SPI系统的波特率
*
* Arguments   : port             SPI0-SPI2 the port to Choose;       选择端口;
*               SPR              0-7,SPI Baud Rate Selection Bits
*               SPPR             0-7,SPI Baud Rate Preselection Bits
*
* Return      : SPI_NO_ERR       if success.
*               SPI_BAD_CH       if invalid port used.
*               SPI_ARGUMENT_OUT if argument out of range.
*
* Note        : BaudRateDivisor = (SPPR + 1) * 2^(SPR + 1)
*               BaudRate = Busclock / BaudRateDivisor
*********************************************************************************************************
*/

unsigned char SPI_setBaudRate(unsigned char port,unsigned char SPR,unsigned char SPPR){
  pSPISTR pPort = pSPI(port);
  portCheck();
  argCheck(SPR,8,SPI_ARGUMENT_OUT);
  argCheck(SPPR,8,SPI_ARGUMENT_OUT);
  SPI_SPR_set_internal(pPort,SPR);
  SPI_SPPR_set_internal(pPort,SPPR);
  return SPI_NO_ERR;
}

/*
*********************************************************************************************************
*                                        SPI_PutChar()
*
* Description : Transmit one char    发送一个字符/字节
*
* Arguments   : port             SPI0-SPI2  the port to Choose;    选择端口;
*               c                the char to transmit;             要发送的字符; 
*
* Return      : SPI_NO_ERR       if success.
*               SPI_BAD_CH       if invalid port used.
*             
* Note:   1. this function will block until transmiter idle and transmit the char;
*              这个函数会阻塞到发送器空闲,然后发送字符
*********************************************************************************************************
*/

unsigned char SPI_PutChar(unsigned char port, unsigned char c){
  portCheck();
  SPI_waitUntilTxReady(pSPI(port));
  SPI_putChar_internal(pSPI(port),c);
  return SPI_NO_ERR;
}
/*
*********************************************************************************************************
*                                        SPI_DataHasReceived()
*
* Description : To check whether there is a data in receive buffer.  检测是否接收寄存器中有数据
*
* Arguments   : port               SPI0-SPI2 the port to Choose.       选择端口.
*                       
* Return      : 0xEE               port invalid.                      端口错误     
*               TRUE               there is data in receive buffer.   接收寄存器内有数据
*               FALSE              receive buffer is empty.           接受寄存器为空
*
* Note:  1. this function just check whether there is a data now, and then return;
*********************************************************************************************************
*/
unsigned char SPI_DataHasReceived(unsigned char port){
  portCheck2(0xEE);
  return !!SPI_isDataReceived(pSPI(port));
}

/*
*********************************************************************************************************
*                                        SPI_GetChar()
*
* Description : Receive one char    接收一个字符/字节
*
* Arguments   : port               SPI0-SPI2 the port to Choose;       选择端口;
*                       
* Return      : 0xEE               port invalid or the char received is 0xEE;   端口错误或接收到的字符正好是0xEE;       
*               others             the char received.                           接收到的字符
*
* Note:   1.this function will block until receiver get a char,and return the char;
*              这个函数会阻塞到接收器寄存器满,然后返回字符
*********************************************************************************************************
*/

unsigned char SPI_GetChar(unsigned char port){
  portCheck2(0xEE);
  SPI_waitUntilDataReceived(pSPI(port));
  return SPI_getChar_internal(pSPI(port));
}

/*
*********************************************************************************************************
*                                        SPI_ExchangeChar()
*
* Description : Exchange one char with the peer.    接收一个字符/字节
*
* Arguments   : port               SPI0-SPI2 the port to Choose;       选择端口;
*               c                  the char to transmit;             要发送的字符; 
*
* Return      : 0xEE               port invalid or the char received is 0xEE;   端口错误或接收到的字符正好是0xEE;       
*               others             the char received.                           接收到的字符
*
* Note:   1.this function will block until receiver get a char,and return the char;
*              这个函数会阻塞到接收器寄存器满,然后返回字符
*********************************************************************************************************
*/

unsigned char SPI_ExchangeChar(unsigned char port, unsigned char c){
  pSPISTR pPort = pSPI(port);
  portCheck2(0xEE);
  SPI_waitUntilTxReady(pPort);
  SPI_putChar_internal(pPort,c);
  SPI_waitUntilDataReceived(pPort);
  return SPI_getChar_internal(pPort);
} 

#endif  // of SPI_MODULE_EN == TRUE

SPI与SCI有点区别,它是同步通信,也就是说每发出去一个字符一定会接收到一个字符,这就产生了非常蛋疼的主机从机怎么协调的问题:从机为了发送数据,必须提前先把数据写入寄存器,然后等待主机的时钟信号,在发送成功的同时收到主机发来的数据;而主机为了收到从机答复的数据,即使你没有数据要给从机,也必须往从机发送数据,才能一一对应的接收到从机发来的数据。

还有一点要注意的是,SPI是双缓冲的,换句话说,如果你的SPI主机连续发了三个字节,然后在提取字节的话可能会有多种情况(也可能分析有误或者不全面,敬请指出)。
1. 如果你提取的足够快,最后一个字符才刚放入发送寄存器还没有发出去,那你会先提取到第一个字节对应的返回字节,然后立刻可以提取第二个字节对应的返回字节。然而提取完后不一定立刻能提取第三个字节对应的返回字节,也可能传输的很快,直接就能提取了,也可能要过一会才能传输完成,然后才能提取。
2. 如果慢了一点,最后一个字节已经发出去了,那第二个字节对应的返回字节就会把第一个字节对应的给挤掉,所以你直接提取到的就是第二个字节对应的返回字节了。然后第三个能不能立刻提取到同上分析。

所以如果你只需要第三个字节对应的返回字节,那一种办法就是发完三个字节后等待足够长的时间,然后提取两个字节。第二个就是。另一种办法就是确保发送和接收一一对应,不发生丢数据的情况。

可以使用提供的SPI_DataHasReceived函数来检查是否接收寄存器中有数据,然后去提取,其他几个方法SPI_GetChar、SPI_PutChar和SPI_ExchangeChar都是阻塞式的方法,直到完成任务才会返回。

对于SPI主机,只要你保证全部都使用SPI_ExchangeChar来发送或接收数据就能保证数据的同步性。而对于SPI从机,你还需要保证在主机发送数据前已经调用了SPI_ExchangeChar。 或者就只能用等待足够长的时间的方法来保证数据同步性了。

使用示例:
先到SPI.h中进行各种初始化设置,然后初始化后使用SPI_ExchangeChar阻塞循环交互数据:

static  void  AppTaskStart (void *p_arg)
{
  unsigned char strSend[] = "Tran:a\n";
  #define STR_SEND_SIZE  (sizeof(strSend)/sizeof(strSend[0]) - 1)
  unsigned char strRecv[] = "Recv:a\n";
  #define STR_RECV_SIZE  (sizeof(strRecv)/sizeof(strRecv[0]) - 1)
  unsigned char dataSend = 'A';
  unsigned char dataRecv;
  (void)p_arg;                                            /* Prevent compiler warning    */
  BSP_Init();
  Console_Init();
  Console_Clear();
  SPI_Init(SPI0);
  SPI_Enable(SPI0);
  while (DEF_TRUE) 
  {
     strSend[5] = dataSend;
     Console_PutChars_Mutex(strSend,STR_SEND_SIZE);
     dataRecv = SPI_ExchangeChar(SPI0,dataSend++);
     strRecv[5] = dataRecv;
     Console_PutChars_Mutex(strRecv,STR_RECV_SIZE);
     if(dataSend > 'Z')
       dataSend = 'A';
     OSTimeDlyHMSM(0,0,1,0);
   }
}

其中 Console是我自己写的使用LCD屏的模拟控制台显示的模块,没有公开出来,当成printf来看就行了。

猜你喜欢

转载自blog.csdn.net/lin_strong/article/details/79361503