[嵌入式開發模塊]MC9S12XEP100 SPI模塊 驅動程序

版權聲明:本文爲博主(http://blog.csdn.net/lin_strong)原創文章,轉載請標明出處並事先取得博主同意 https://blog.csdn.net/lin_strong/article/details/79361503

之前發了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來看就行了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章