不同电平类型单片机之间的SPI通信

2020-01-18 18:44发布

本帖最后由 xxguo 于 2012-10-2 20:50 编辑

现有STC12C5616和CC2530两个单片机。需要两个单片机SPI通信(5616为master,2530为slaver),请问现在有哪些需要注意的问题呢?
我已经实现5616和5616之间的SPI以及2530与2530之间的SPI通信。但是交叉之后就是没有反应,不知道可能是哪里的问题呢?
/*******************************************************************************
date   : 2010/08/29
writer :on the way..

Port   :P0_2,P0_3,P0_4,P0_5    这四个端口是用于UART0外设配置
         P1_0,P1_1            这两个端口是LED1和LED2
         P1_4,P1_5,P1_6,P1_7    这四个端口用于SPI通信
fuction:SPI_DMA中断方式通信
         从模式:配置好DMA的参数,使能DMA中断之后,开启DMA,如
      果串口1有接收到数据,这是会触发DMA,传输数据到指定的数组
      rxBufferSlave中,这里是可以将整个字符串直接传送过来的,最
      后在串口调试助手中显示on the way..
        
*******************************************************************************/
/*--------------------------------------------------------------------------------
    Master                 Slave
-------------          -------------
|           |          |           |
|P1_4   SSN |--------->|SSN    P1_4|
|           |          |           |
|P1_5   SCK |--------->|SCK    P1_5|
|           |          |           |
|P1_6   MOSI|--------->|MOSI   P1_6|
|           |          |           |
|P1_7   MISO|<---------|MISO   P1_7|
|           |          |           |
-------------          -------------
--------------------------------------------------------------------------------*/
// Slave Mode

#include <iocc2530.h>
#include "hal_cc8051.h"

#define LED1          P1_0
#define LED2          P1_1

#define LED_OFF 1
#define LED_ON  0

#define SSN       P1_4
#define N         13   // Length byte
char rxBufferSlave[13];//接收到的数据放到这个数组中
BOOL sDataReceived;    //接收数据标志位
/******************************************************************************
* @fn  init_port
*
* @brief
*     Initializes components IO port application example.
*
* Parameters:
*
* @param  void
*
* @return void
*
******************************************************************************/
void init_port(void)
{
   IO_FUNC_PORT_PIN(1, 0, IO_FUNC_GIO);    //将P1_0设置为普通的IO口
   IO_DIR_PORT_PIN(1, 0, IO_OUT);          //设置为输出
   
   IO_FUNC_PORT_PIN(1, 1, IO_FUNC_GIO);    //将P1_1设置为普通的IO口
   IO_DIR_PORT_PIN(1, 1, IO_OUT);          //设置为输出
   
   PERCFG |= 0x02;        // PERCFG.U1CFG = 1
   P1SEL |= 0xF0;         // P1_7, P1_6, P1_5, and P1_4 are peripherals
   
}

void init_Baudrate(void)
{
  // a 32 MHz crystal,
  // max baud rate = 32 MHz / 8 = 4 MHz.  
  U1BAUD = 0x00;   // BAUD_M = 0
  U1GCR |= 0x11;   // BAUD_E = 17
}
/******************************************************************************
* @fn  initUART
*
* @brief
*      Initializes components for the UART application example.
*
* Parameters:
*
* @param  void
*
* @return void
*
******************************************************************************/
void initUART(void)
{
   // Setup for UART0
   IO_PER_LOC_UART0_AT_PORT0_PIN2345();//将端口0的2345设置为外设
                  
   //这里的串口0设置为:波特率57600,没有校验位,8位数据,1位停止位,
   UART_SETUP(0, 57600, HIGH_STOP);    //设置串口
}
/*******************************************************************************
* @fn          main
*
* @brief      
*
* @param       none
*
* @return      none
*******************************************************************************/
void main(void)
{
  uint8 i;
  halMcuInit();     //设置时钟源32MHZ
  init_port();      //初始化端口
  init_Baudrate();  //初始化波特率
  initUART();       //初始化UART
  
  // SPI Slave Mode
  U1CSR &= ~0x80;   
  U1CSR |= 0x20;
  
  // Configure phase, polarity, and bit order
  U1GCR |= 0xC0;   // CPOL = CPHA = 1
  U1GCR &=~0x20;    // ORDER = 1
  
  // 1. Clear interrupt flags
  // For pulsed or edge shaped interrupt sources one should clear the CPU interrupt
  // flag prior to clearing the module interrupt flag
  TCON &= ~0x80;
  
  // 2. Set individual interrupt enable bit in the peripherals SFR, if any
  
  // 3. Set the corresponding individual, interrupt enable bit in the IEN0, IEN1, or
  // IEN2 registers to 1
  URX1IE = 1;
  
  
  // SPI Slave
  //-------------------------------------------------------------------------------
  DMA_DESC  dmaConfigRx;
  
  SET_WORD(dmaConfigRx.SRCADDRH, dmaConfigRx.SRCADDRL, &X_U1DBUF);
  SET_WORD(dmaConfigRx.DESTADDRH, dmaConfigRx.DESTADDRL, rxBufferSlave);
  dmaConfigRx.VLEN      = 0;  // Transfer number of bytes commanded by n, + 1
  SET_WORD(dmaConfigRx.LENH, dmaConfigRx.LENL, N + 1); //LEN = nmax + 1
  dmaConfigRx.WORDSIZE  = 0;  // Each transfer is 8 bits
  dmaConfigRx.TRIG      = 17; // Use URX1 trigger 17
  dmaConfigRx.TMODE     = 0;  // One byte transferred per trigger event 0
  dmaConfigRx.SRCINC    = 1;  // Keep the same source addr. for all transfers 0
  dmaConfigRx.DESTINC   = 0;  // Increase dest. addr. by 1 between transfers 1
  dmaConfigRx.IRQMASK   = 1;  // Allow IRCON.DMAIF to be asserted when the transfer 1
  // count is reached
  dmaConfigRx.M8        = 0;  // Use all 8 bits of first byte in source data to
  // determine the transfer count
  dmaConfigRx.PRIORITY  = 2;  // DMA memory access has high priority
  
  // Save pointer to the DMA config. struct into DMA ch. 0 config. registers
  SET_WORD(DMA0CFGH, DMA0CFGL, &dmaConfigRx);
  //-------------------------------------------------------------------------------
  
  //-------------------------------------------------------------------------------
  // 1. Clear interrupt flags
  // For pulsed or edge shaped interrupt sources one should clear the CPU interrupt
  // flag prior to clearing the module interrupt flag
  DMAIF = 0;          //清除DMA中断标志位
  DMAIRQ &= ~0x01;   
  
  // 2. Set individual interrupt enable bit in the peripherals SFR, if any
  // No flag for the DMA (Set in the DMA struct (IRQMASK = 1))
  
  // 3. Set the corresponding individual, interrupt enable bit in the IEN0, IEN1, o
  // IEN2 registers to 1
  DMAIE = 1;  //使能DMA中断
  
  // 4. Enable global interrupt
  EA = 1;     //使能总中断
  //-------------------------------------------------------------------------------
  
  //-------------------------------------------------------------------------------
  DMAARM = 0x01;   //使得DMA通道0处于准备好的状态,如果有数据接收到,就会执行中断语句
  
  while (1)  
  {
    if (sDataReceived)
    {
      sDataReceived = FALSE; // All 10 bytes are received
      for(i=0;i<sizeof(rxBufferSlave)+1;i++)
      {
        U0DBUF = rxBufferSlave;
        while(UTX0IF == 0);    //等待发送完毕
        UTX0IF = 0;
      }
    }
  }  
}


#pragma vector=DMA_VECTOR
  __interrupt void dma_IRQ(void)
{
    DMAIF = 0;               // Clear the CPU DMA interrupt flag
    DMAIRQ &= ~0x01;       // DMA channel 0 module interrupt flag
    sDataReceived = TRUE;    // All 10 bytes are received
}
//-------------------------------------------------------------------------------
  
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
24条回答
xxguo
1楼-- · 2020-01-19 10:58
tony90 发表于 2012-10-2 23:00
这两种单片机都没用过,但如果是电平不同的话,应该增加一个电平转换芯片啊。我以前用DSP的时候要控制12864 ...

一个是5v的一个是3.3V的,非要加那个吗?
tony90
2楼-- · 2020-01-19 11:20
 精彩回答 2  元偷偷看……
xxguo
3楼-- · 2020-01-19 14:57
问题已经解决了,发上来大家瞧瞧~~~
功能   : STC12C5616 与 CC2530 单片机之间进行SPI通信,交换部分数据。
         从串口调试助手把一个八位的数据 发到Master上,Master接收到数据之后,通过SPI把数据发送给Slaver。与此同时Slaver直接又把另一组数据转发给Master,然后Master把接收到的数据发给上位机。然后Slaver把从Master那里接受来的数据发送到另一个串口调试助手!

Master : STC12C5616
Slaver : CC2530
连接:

    Master                 Slave
-----------------------------------------------------
|                  |            |                   |
|P1_4   SSN   |--------->| SSN    P1_4  |
|                  |           |                    |
|P1_7   SCK   |--------->| SCK    P1_5  |
|                  |            |                   |
|P1_5   MOSI |--------->| MOSI   P1_6 |
|                  |            |                   |
|P1_6   MISO |<---------| MISO   P1_7 |
|                  |            |                   |
-----------------------------------------------------

注意事项:虽然两种单片机之间的逻辑电平不一致,但是5V的作为主机,3.3V的作为从机,并不影响,依然可以正常通信。
          但是记得,两个单片机之间要公地!!!
xxguo
4楼-- · 2020-01-19 19:04
本帖最后由 xxguo 于 2012-10-3 15:37 编辑

Master 依然不变



#include "reg51.h"
#define SPIF  ( SPSTAT & 0x80 )  //查询SPIF是否置位,SPIF不能进行位操作
/*通信端口设置-------------------------------------------------------*/

/**********************************************************************
定义寄存器
************************************************************************/
sfr       AUXR    =   0x8e;           //Auxiliary register

sfr       SPSTAT  =   0x84;           //SPI status register  1000 0100
//#define   SPIF        0x80            //SPSTAT.7
#define   WCOL        0x40            //SPSTAT.6

sfr       SPCTL   =   0x85;           //SPI control register 1000 0101  master  SPI禁止 MSB先发 主模式  。。。16分频
#define   SSIG        0x80            //SPCTL.7
#define   SPEN        0x40            //SPCTL.6
#define   DORD        0x20            //SPCTL.5
#define   MSTR        0x10            //SPCTL.4
#define   CPOL        0x08            //SPCTL.3
#define   CPHA        0x04            //SPCTL.2
#define   SPDHH       0x00            //CPU_CLK/4
#define   SPDH        0x01            //CPU_CLK/16
#define   SPDL        0x02            //CPU_CLK/64
#define   SPDLL       0x03            //CPU_CLK/128

sfr       SPDAT   =   0x86;           //SPI data register
/////////////////////////////////////////////////////////////////////
sbit SCLK = P1^7;
sbit MISO = P1^6;
sbit MOSI = P1^5;
sbit SS   = P1^4;
unsigned char UART_SendData=0;
unsigned char UART_RecData=0;
unsigned char SPI_RecData=0;
void Init_System();
void Init_UART();
void Init_SPI();
unsigned char SPI_SendByte(unsigned char SPI_SendData);
void UART_SendByte(unsigned char UART_Send);
void delay_ms(unsigned int s);
/*---------------------------------------------------------
*函数名:main
*函数功能:系统初始化
*输入参数:无
*返回参数:无
----------------------------------------------------------*/
void main()
{
        Init_System();
        while(1)           
        {
                /*  查询UART接收信号 ----------------------------------*/
                while(!RI);     //查看串口是否接收到数据
                {
                        RI=0;               //当接收到数据后,清除接收中断标志
                        UART_RecData=SBUF;   //读入数据
                        UART_SendData=SPI_SendByte(UART_RecData);//将收到的数据由SPI发送出去,又接收回来
                        UART_SendByte(UART_SendData);
                }
        }
}
/*---------------------------------------------------------
*函数名:Init_System
*函数功能:系统初始化
*输入参数:无
*返回参数:无
----------------------------------------------------------*/
void Init_System()
{
        Init_UART();
        Init_SPI();
}
/*---------------------------------------------------------
*函数名:Init_UART
*函数功能:串口初始化,无中断方式
*输入参数:无
*返回参数:无
----------------------------------------------------------*/
void Init_UART()
{
        TMOD=0x20;//设置定时器1为工作方式2
        AUXR|=0x40;//不对定时器1分频,速度是普通单片机的12倍
        TH1=0xd9; //波特率为9600,晶振为12MHz
        TL1=0xd9;
        TR1=1;   //启动T1
        REN=1;   //串行允许位
        //  PCON=0x80;  //PCON寄存器的SMOD位置一,波特率提高一倍
        SM0=0;
        SM1=1;   //串行方式1
        // ES=1;    //中断接收则开串口中断,查询接收则关闭此句
}
/*---------------------------------------------------------
*函数名:Init_SPI
*函数功能:SPI初始化,SPI的工作方式,不使用SPI中断方式
*输入参数:无
*返回参数:无
----------------------------------------------------------*/
void Init_SPI()
{
        SPCTL = 0xff;//CPU_CLK/16,时钟前沿为下降沿,后沿采样,主模式,最低位在前,SPI使能 1111 1111
        SPSTAT = 0xc0;//SPDAT.7和SPDAT.6写11,可以将中断标志清零。注意是写1才清零
}
/*---------------------------------------------------------
*函数名:SPI_SendByte
*函数功能:SPI发射接收一体程序
*输入参数:SPI_SendData,经主机发给从机的数据
*返回参数:从机发给主机的数据
----------------------------------------------------------*/
unsigned char SPI_SendByte(unsigned char SPI_SendData)
{
        SS=0;
        delay_ms(2);
        SPDAT= SPI_SendData; //将串口接收的数据装入SPI数据寄存器
        while(!SPIF);//等待发送完毕       
        delay_ms(2);       
        SS=1;
        SPSTAT = 0xc0; //清除中断标志,和写冲突标志,注意是对应位写1才能清零
        SPI_RecData = SPDAT;
        return  SPI_RecData;
}
/*---------------------------------------------------------
*函数名:UART_SendByte
*函数功能:串口发送
*输入参数:UART_Send
*返回参数:无
----------------------------------------------------------*/
void UART_SendByte(unsigned char UART_Send)
{
        TI = 0;            //清除发送SBUF空标志
        SBUF =  UART_Send; //写入SBUF
        while (!TI);       //等待发送完毕
        TI = 0;            //清除发送SBUF空标志
}  

void delay_ms(unsigned int s) //static
{
        unsigned int i;
        for(;s*12>0;s--)                                //STC12C5616  需要*12
                for(i=12000;i>0;i--);        //STC12C5616  需要*12
}
xxguo
5楼-- · 2020-01-19 23:02
本帖最后由 xxguo 于 2012-10-3 15:41 编辑

Slaver 取消用DMA,直接用SPI收发数据就可以了~~~



// Slave Mode

#include <iocc2530.h>
#include "hal_cc8051.h"

#define LED1          P1_0
#define LED2          P1_1

#define LED_OFF 1
#define LED_ON  0

#define SSN       P1_4
/******************************************************************************
* @fn  init_port
*
* @brief
*     Initializes components IO port application example.
*
* Parameters:
*
* @param  void
*
* @return void
*
******************************************************************************/
void init_port(void)
{
   IO_FUNC_PORT_PIN(1, 0, IO_FUNC_GIO);    //将P1_0设置为普通的IO口
   IO_DIR_PORT_PIN(1, 0, IO_OUT);          //设置为输出
   
   IO_FUNC_PORT_PIN(1, 1, IO_FUNC_GIO);    //将P1_1设置为普通的IO口
   IO_DIR_PORT_PIN(1, 1, IO_OUT);          //设置为输出
   
   PERCFG |= 0x02;        // PERCFG.U1CFG = 1
   P1SEL |= 0xF0;         // P1_7, P1_6, P1_5, and P1_4 are peripherals
   
}

void init_Baudrate(void)
{
  // Set baud rate to max (system clock frequency / 8)
  // a 32 MHz crystal,
  // max baud rate = 32 MHz / 8 = 4 MHz.  
  U1BAUD = 0x00;   // BAUD_M = 0
  U1GCR |= 0x11;   // BAUD_E = 17
}
/******************************************************************************
* @fn  initUART
*
* @brief
*      Initializes components for the UART application example.
*
* Parameters:
*
* @param  void
*
* @return void
*
******************************************************************************/
void initUART(void)
{
   // Setup for UART0
   IO_PER_LOC_UART0_AT_PORT0_PIN2345();//将端口0的2345设置为外设
                  
   //这里的串口0设置为:波特率57600,没有校验位,8位数据,1位停止位,
   UART_SETUP(0, 57600, HIGH_STOP);    //设置串口
}
/*******************************************************************************
* @fn          main
*
* @brief      
*
* @param       none
*
* @return      none
*******************************************************************************/
void main(void)
{
  halMcuInit();// 选择32MHz晶体振荡器作为系统时钟源(主时钟源)//设置时钟源32MHZ
  init_port();      //初始化端口
  init_Baudrate();  //初始化波特率
  initUART();       //初始化UART

  // SPI Slave Mode
  U1CSR &= ~0x80;
  U1CSR |= 0x20;
  
  // Configure phase, polarity, and bit order
  U1GCR |= 0xC0;   // CPOL = CPHA = 1
  U1GCR &=~0x20;    // ORDER = 0 LSB First
  
  
//  LED1=LED_ON;      //灯亮表示进入接收状态
  
  
  char rxBufferSlave[1];
  char _RSSI[1];
  _RSSI[0]=0x13;
////////////////////////////////////////////////////////////////////////////////////////////////////////
//  unsigned char i;
/*  for (i = 0; i < sizeof(rxBufferSlave); i++)  
  {
    while (!U1RX_BYTE);
    U1RX_BYTE = 0;
    rxBufferSlave = U1DBUF;
    LED1=~LED1;
  }
  
   
  LED1=LED_OFF;     //灯灭表示数据已经传送完,接着在串口上显示
  
  for(i=0;i<sizeof(rxBufferSlave);i++)
  {
    U0DBUF = rxBufferSlave;
    while(UTX0IF == 0);    //等待发送完毕
    UTX0IF = 0;
  }*/
/////////////////////////////////////////////////////////////////////////////////////////////
  while(1)
  {
    while (!U1RX_BYTE);
    rxBufferSlave[0] = U1DBUF;
    U1DBUF=_RSSI[0];
//    U1RX_BYTE = 0;
    U0DBUF = rxBufferSlave[0];
    while(UTX0IF == 0);    //等待发送完毕
    UTX0IF = 0;
  }
  
}
xxguo
6楼-- · 2020-01-20 03:18
然后关于SPI比较有参考性的就是摩托罗拉公司的SPI 规范协议了~~~

一周热门 更多>