如何從Cortex-m向STM32移植使用SPI介面協議
阿新 • • 發佈:2019-02-19
/*****************************************************************************************************
* @brief:LDC1000應用程式
* ____________________________
* |PB4(SSI2CLK)---->SCLK|
* |PB5(SSI2FSS)---->CSB |
* |PB6(SSI2RX) <---- SDO|
* Tiva M4|PB7(SSI2TX)---->SDI|LDC1000
* |PA4(INT/GPIO)<----INTB|
* |PB0(Timer CLK)----> TBCLK|
* _____________||______________
*****************************************************************************************************/
#include <stdint.h>
#include <stdbool.h>
#include "driverlib/rom.h"
#include "driverlib/adc.h"
#include "driverlib/sysctl.h"
#include "driverlib/pwm.h"
#include "driverlib/timer.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/interrupt.h"
#include "driverlib/ssi.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_gpio.h"
#include "LDC1000_cmd.h"
#include "inc/hw_timer.h"
#include "inc/hw_types.h"
#include "inc/hw_ssi.h"
#ifndef TARGET_IS_BLIZZARD_RA1
#define TARGET_IS_BLIZZARD_RA1
#endif
#ifndef PART_TM4C123GH6PM
#define PART_TM4C123GH6PM
#endif
#define SPI_RWBIT 0x80 //LDC1000,SPI時序讀寫位,1=讀,0=寫
unsigned long ProximityData; //LDC上Proximity Data
unsigned long FrequencyData; //LDC1000上的Frequency Data
volatile unsigned char DataRdy ; //LDC1000中斷標誌
uint32_t DataRcv[5] ; //儲存SPI讀取的資料
/********************************************************************
* @brief:SPI寫資料
* @param:unsigned int,SPIdata:待寫的資料
* @return:none
*********************************************************************/
void SPIDataSend(unsigned int SPIdata)
{
SSIDataPut(SSI2_BASE,SPIdata); //SPI傳送(寫)資料
while(SSIBusy(SSI2_BASE)) ; //等待SPI傳送(寫)完成
}
/********************************************************************
* @brief:LDC1000初始化配置,ps:在SPI中配置了資料位16個數據長度,故
* 在傳送資料時可以將地址和值進行或運算一起傳送出去
* @param:none
* @return:none
*********************************************************************/
void LDC1000_init()
{
SPIDataSend(LDC1000_CMD_RPMAX<<8|TEST_RPMAX_INIT);//配置Rp_MAX(0x01)暫存器
SPIDataSend(LDC1000_CMD_RPMIN<<8|TEST_RPMIN_INIT);//配置Rp_MIN(0x02)暫存器
SPIDataSend(LDC1000_CMD_SENSORFREQ<<8|0x94);//配置Sensor Frequency(0x03)暫存器
SPIDataSend(LDC1000_CMD_LDCCONFIG<<8|0x17);//配置LDC Configuration(0x04)暫存器
SPIDataSend(LDC1000_CMD_CLKCONFIG<<8|0x00);//配置Clock Configuration(0x05)暫存器,
//使用TBCLK作為時鐘源
SPIDataSend(LDC1000_CMD_INTCONFIG<<8|0x02);//配置INTB Pin Configuration(0x0A),
//配置INTB為比較輸出標誌位(status of Comparator output)
SPIDataSend(LDC1000_CMD_THRESHILSB<<8|0x50);//配置Comparator Threshold High(0x06)暫存器低8位
SPIDataSend(LDC1000_CMD_THRESHIMSB<<8|0x14);//配置Comparator Threshold High(0x07)暫存器高8位
SPIDataSend(LDC1000_CMD_THRESLOLSB<<8|0xC0);//配置Comparator Threshold Low(0x08)暫存器低8位
SPIDataSend(LDC1000_CMD_THRESLOMSB<<8|0x12);//配置Comparator Threshold Low(0x09)暫存器高8位
SPIDataSend(LDC1000_CMD_PWRCONFIG<<8|0x01);//配置Power Configuration(0x0B)暫存器,
//為Active Mode,使能轉化
}
/********************************************************************
* @brief:使用SPI讀取LDC1000中的資料
* @param:none
* @return:none
*********************************************************************/
void LDCRead()
{
SPIDataSend((LDC1000_CMD_PROXLSB|SPI_RWBIT)<<8); //寫入將要讀取的Proximity Data LSB暫存器地址(0x21)
SSIDataGet(SSI2_BASE,&DataRcv[0]); //讀取上述暫存器中的值,並存入DataRcv[0]
ProximityData|= DataRcv[0] ;
SPIDataSend((LDC1000_CMD_PROXMSB|SPI_RWBIT)<<8);//寫入將要讀取的Proximity Data MSB暫存器地址(0x22)
SSIDataGet(SSI2_BASE,&DataRcv[1]);//讀取上述暫存器中的值,並存入DataRcv[1]
ProximityData|= (DataRcv[1]<<8) ;//組合成ProximityData
SPIDataSend((LDC1000_CMD_FREQCTRLSB|SPI_RWBIT)<<8);//寫入將要讀取的Frequency Counter Data LSB暫存器地址(0x23)
SSIDataGet(SSI2_BASE,&DataRcv[2]);//讀取上述暫存器中的值,並存入DataRcv[2]
FrequencyData|= DataRcv[2] ;
SPIDataSend((LDC1000_CMD_FREQCTRMID|SPI_RWBIT)<<8);//寫入將要讀取的Frequency Counter Data Mid-Byte暫存器地址(0x24)
SSIDataGet(SSI2_BASE,&DataRcv[3]);//讀取上述暫存器中的值,並存入DataRcv[3]
FrequencyData|= (DataRcv[3]<<8) ;
SPIDataSend((LDC1000_CMD_FREQCTRMSB|SPI_RWBIT)<<8);//寫入將要讀取的Frequency Counter Data MSB暫存器地址(0x25)
SSIDataGet(SSI2_BASE,&DataRcv[4]);//讀取上述暫存器中的值,並存入DataRcv[4]
FrequencyData|= (DataRcv[4]<<16) ;//組合成FrequencyData
GPIOIntEnable(GPIO_PORTA_BASE,GPIO_INT_PIN_4);//使能PA4中斷
}
/********************************************************************
* @brief:Timer初始化
* @param:none
* @return:none
* ____________________________
* ||
* Tiva M4|PB0(Timer CLK)----> TBCLK|LDC1000
* _____________||______________
*********************************************************************/
void TimerInit()
{
TimerDisable(TIMER2_BASE,TIMER_A);
GPIOPinTypeTimer(GPIO_PORTB_BASE,GPIO_PIN_0);
GPIOPinConfigure(GPIO_PB0_T2CCP0);//配置PB0為CCP模式
HWREG(TIMER2_BASE + TIMER_O_CFG) = 0x04;//選擇16-bit timer
//配置TimerA週期計數(Periodic Timer mode)
HWREG(TIMER2_BASE + TIMER_O_TAMR)|=(TIMER_TAMR_TAAMS|TIMER_TAMR_TAMR_PERIOD) ;
//載入Timer計數值:40,並且設定Match值:20(Timer預設為減計數)
HWREG(TIMER2_BASE + TIMER_O_TAMATCHR) = 20;
TimerLoadSet(TIMER2_BASE,TIMER_A,40);
TimerEnable(TIMER2_BASE,TIMER_A);//使能Timer
}
/********************************************************************
* @brief:PA4初始化
* @param:none
* @return:none
* ____________________
* ||
* ||
* Tiva M4 | PA4 <--- INTB| LDC1000
* ||
* —————————||__________
*********************************************************************/
void GPIOInit()
{
HWREG(GPIO_PORTA_BASE + GPIO_O_DEN) |= 1<<4 ;
GPIOIntTypeSet(GPIO_PORTA_BASE,GPIO_PIN_4,GPIO_RISING_EDGE); //配置為上升沿中斷
GPIOIntEnable(GPIO_PORTA_BASE,GPIO_INT_PIN_4); //使能PA4中斷
IntEnable(INT_GPIOA); //使能GPIOA中斷
}
/********************************************************************
*@brief: SPI通訊初始化
*@param: none
*@return: none
* ____________________________
* |PB4(SSI2CLK)---->SCLK|
* |PB5(SSI2FSS)---->CSB |
* |PB6(SSI2RX) <---- SDO|
* Tiva M4|PB7(SSI2TX)---->SDI|LDC1000
* |PB0(Timer CLK)----> TBCLK|
* _____________||______________
*********************************************************************/
void SPIInit()
{
//配置PB6為SSI2RX,即對Tiva M4而言的SPI資料接收線
GPIOPinTypeSSI(GPIO_PORTB_BASE,GPIO_PIN_6) ;
GPIOPinConfigure(GPIO_PB6_SSI2RX);
//配置PB6為SSI2TX,即對Tiva M4而言的SPI資料傳送線
GPIOPinTypeSSI(GPIO_PORTB_BASE,GPIO_PIN_7) ;
GPIOPinConfigure(GPIO_PB7_SSI2TX);
//配置PB4為SSI2CLK線,作為時鐘線
GPIOPinTypeSSI(GPIO_PORTB_BASE,GPIO_PIN_4) ;
GPIOPinConfigure(GPIO_PB4_SSI2CLK);
//配置PB5為SSI2FFS線,作為片選線
GPIOPinTypeSSI(GPIO_PORTB_BASE,GPIO_PIN_5) ;
GPIOPinConfigure(GPIO_PB5_SSI2FSS);
SSIDisable(SSI2_BASE); //禁能SSI2
//配置SSI2為SSI_FRF_MOTO_MODE_0協議格式,SPI主模式,時鐘源為5K,16位資料長度
SSIConfigSetExpClk(SSI2_BASE,SysCtlClockGet(),SSI_FRF_MOTO_MODE_0,SSI_MODE_MASTER,5000,16);
SSIEnable(SSI2_BASE) ;//使能SSI2
}
/********************************************************************
*@brief: PA4中斷服務函式,該函式在startup_ccs.c的中斷向量表中進行
*了註冊
*@param: none
*@return: none
* ____________________
* ||
* ||
* Tiva M4 | PA4 <--- INTB| LDC1000
* ||
* —————————||__________
*********************************************************************/
void GPIOAIntHandler()
{
GPIOIntClear(GPIO_PORTA_BASE,GPIO_INT_PIN_4) ;//清除PA4中斷標誌
DataRdy = 1 ; //LDC1000中斷標誌置位
GPIOIntDisable(GPIO_PORTA_BASE,GPIO_INT_PIN_4) ;//禁能PA4中斷,將在SPI資料讀取完成後重新使能PA4中斷
}
//---------------------------------------main函式----------------------------------------------
int main(void)
{
SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ |//配置主時鐘為50MHz
SYSCTL_OSC_MAIN);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA) ;//使能GPIOA外設模組
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB) ; //使能GPIOB外設模組
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2); //使能Timer2外設模組
SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI2); //使能SSI2外設模組
DataRdy = 0 ;//LDC1000中斷標誌清零
TimerInit() ;//TBCLK所在時鐘初始化
GPIOInit() ;//GPIO初始化(PA4)
SPIInit() ;//SPI初始化
LDC1000_init();//LDC1000初始配置
ROM_IntMasterEnable(); //使能總中斷
while(HWREG(SSI2_BASE + SSI_O_SR)& SSI_SR_RNE) //首先清除SPI上的接收快取,排除干擾
{
DataRcv[0] = HWREG(SSI2_BASE + SSI_O_DR) ;
}
while(1)
{
//轉化結束後讀取ProximityData和FrequencyData
if(DataRdy)
{
DataRdy = 0 ;//LDC1000中斷標誌清零(在PA4中斷服務程式中置位)
LDCRead() ;//SPI讀取資料操作
}
}
* @brief:LDC1000應用程式
* ____________________________
* |PB4(SSI2CLK)---->SCLK|
* |PB5(SSI2FSS)---->CSB |
* |PB6(SSI2RX) <---- SDO|
* Tiva M4|PB7(SSI2TX)---->SDI|LDC1000
* |PA4(INT/GPIO)<----INTB|
* |PB0(Timer CLK)----> TBCLK|
* _____________||______________
*****************************************************************************************************/
#include <stdint.h>
#include <stdbool.h>
#include "driverlib/rom.h"
#include "driverlib/adc.h"
#include "driverlib/sysctl.h"
#include "driverlib/pwm.h"
#include "driverlib/timer.h"
#include "driverlib/gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/interrupt.h"
#include "driverlib/ssi.h"
#include "inc/hw_ints.h"
#include "inc/hw_memmap.h"
#include "inc/hw_gpio.h"
#include "LDC1000_cmd.h"
#include "inc/hw_timer.h"
#include "inc/hw_types.h"
#include "inc/hw_ssi.h"
#ifndef TARGET_IS_BLIZZARD_RA1
#define TARGET_IS_BLIZZARD_RA1
#endif
#ifndef PART_TM4C123GH6PM
#define PART_TM4C123GH6PM
#endif
#define SPI_RWBIT 0x80 //LDC1000,SPI時序讀寫位,1=讀,0=寫
unsigned long ProximityData; //LDC上Proximity Data
unsigned long FrequencyData; //LDC1000上的Frequency Data
volatile unsigned char DataRdy ; //LDC1000中斷標誌
uint32_t DataRcv[5] ; //儲存SPI讀取的資料
/********************************************************************
* @brief:SPI寫資料
* @param:unsigned int,SPIdata:待寫的資料
* @return:none
*********************************************************************/
void SPIDataSend(unsigned int SPIdata)
{
SSIDataPut(SSI2_BASE,SPIdata); //SPI傳送(寫)資料
while(SSIBusy(SSI2_BASE)) ; //等待SPI傳送(寫)完成
}
/********************************************************************
* @brief:LDC1000初始化配置,ps:在SPI中配置了資料位16個數據長度,故
* 在傳送資料時可以將地址和值進行或運算一起傳送出去
* @param:none
* @return:none
*********************************************************************/
void LDC1000_init()
{
SPIDataSend(LDC1000_CMD_RPMAX<<8|TEST_RPMAX_INIT);//配置Rp_MAX(0x01)暫存器
SPIDataSend(LDC1000_CMD_RPMIN<<8|TEST_RPMIN_INIT);//配置Rp_MIN(0x02)暫存器
SPIDataSend(LDC1000_CMD_SENSORFREQ<<8|0x94);//配置Sensor Frequency(0x03)暫存器
SPIDataSend(LDC1000_CMD_LDCCONFIG<<8|0x17);//配置LDC Configuration(0x04)暫存器
SPIDataSend(LDC1000_CMD_CLKCONFIG<<8|0x00);//配置Clock Configuration(0x05)暫存器,
//使用TBCLK作為時鐘源
SPIDataSend(LDC1000_CMD_INTCONFIG<<8|0x02);//配置INTB Pin Configuration(0x0A),
//配置INTB為比較輸出標誌位(status of Comparator output)
SPIDataSend(LDC1000_CMD_THRESHILSB<<8|0x50);//配置Comparator Threshold High(0x06)暫存器低8位
SPIDataSend(LDC1000_CMD_THRESHIMSB<<8|0x14);//配置Comparator Threshold High(0x07)暫存器高8位
SPIDataSend(LDC1000_CMD_THRESLOLSB<<8|0xC0);//配置Comparator Threshold Low(0x08)暫存器低8位
SPIDataSend(LDC1000_CMD_THRESLOMSB<<8|0x12);//配置Comparator Threshold Low(0x09)暫存器高8位
SPIDataSend(LDC1000_CMD_PWRCONFIG<<8|0x01);//配置Power Configuration(0x0B)暫存器,
//為Active Mode,使能轉化
}
/********************************************************************
* @brief:使用SPI讀取LDC1000中的資料
* @param:none
* @return:none
*********************************************************************/
void LDCRead()
{
SPIDataSend((LDC1000_CMD_PROXLSB|SPI_RWBIT)<<8); //寫入將要讀取的Proximity Data LSB暫存器地址(0x21)
SSIDataGet(SSI2_BASE,&DataRcv[0]); //讀取上述暫存器中的值,並存入DataRcv[0]
ProximityData|= DataRcv[0] ;
SPIDataSend((LDC1000_CMD_PROXMSB|SPI_RWBIT)<<8);//寫入將要讀取的Proximity Data MSB暫存器地址(0x22)
SSIDataGet(SSI2_BASE,&DataRcv[1]);//讀取上述暫存器中的值,並存入DataRcv[1]
ProximityData|= (DataRcv[1]<<8) ;//組合成ProximityData
SPIDataSend((LDC1000_CMD_FREQCTRLSB|SPI_RWBIT)<<8);//寫入將要讀取的Frequency Counter Data LSB暫存器地址(0x23)
SSIDataGet(SSI2_BASE,&DataRcv[2]);//讀取上述暫存器中的值,並存入DataRcv[2]
FrequencyData|= DataRcv[2] ;
SPIDataSend((LDC1000_CMD_FREQCTRMID|SPI_RWBIT)<<8);//寫入將要讀取的Frequency Counter Data Mid-Byte暫存器地址(0x24)
SSIDataGet(SSI2_BASE,&DataRcv[3]);//讀取上述暫存器中的值,並存入DataRcv[3]
FrequencyData|= (DataRcv[3]<<8) ;
SPIDataSend((LDC1000_CMD_FREQCTRMSB|SPI_RWBIT)<<8);//寫入將要讀取的Frequency Counter Data MSB暫存器地址(0x25)
SSIDataGet(SSI2_BASE,&DataRcv[4]);//讀取上述暫存器中的值,並存入DataRcv[4]
FrequencyData|= (DataRcv[4]<<16) ;//組合成FrequencyData
GPIOIntEnable(GPIO_PORTA_BASE,GPIO_INT_PIN_4);//使能PA4中斷
}
/********************************************************************
* @brief:Timer初始化
* @param:none
* @return:none
* ____________________________
* ||
* Tiva M4|PB0(Timer CLK)----> TBCLK|LDC1000
* _____________||______________
*********************************************************************/
void TimerInit()
{
TimerDisable(TIMER2_BASE,TIMER_A);
GPIOPinTypeTimer(GPIO_PORTB_BASE,GPIO_PIN_0);
GPIOPinConfigure(GPIO_PB0_T2CCP0);//配置PB0為CCP模式
HWREG(TIMER2_BASE + TIMER_O_CFG) = 0x04;//選擇16-bit timer
//配置TimerA週期計數(Periodic Timer mode)
HWREG(TIMER2_BASE + TIMER_O_TAMR)|=(TIMER_TAMR_TAAMS|TIMER_TAMR_TAMR_PERIOD) ;
//載入Timer計數值:40,並且設定Match值:20(Timer預設為減計數)
HWREG(TIMER2_BASE + TIMER_O_TAMATCHR) = 20;
TimerLoadSet(TIMER2_BASE,TIMER_A,40);
TimerEnable(TIMER2_BASE,TIMER_A);//使能Timer
}
/********************************************************************
* @brief:PA4初始化
* @param:none
* @return:none
* ____________________
* ||
* ||
* Tiva M4 | PA4 <--- INTB| LDC1000
* ||
* —————————||__________
*********************************************************************/
void GPIOInit()
{
HWREG(GPIO_PORTA_BASE + GPIO_O_DEN) |= 1<<4 ;
GPIOIntTypeSet(GPIO_PORTA_BASE,GPIO_PIN_4,GPIO_RISING_EDGE); //配置為上升沿中斷
GPIOIntEnable(GPIO_PORTA_BASE,GPIO_INT_PIN_4); //使能PA4中斷
IntEnable(INT_GPIOA); //使能GPIOA中斷
}
/********************************************************************
*@brief: SPI通訊初始化
*@param: none
*@return: none
* ____________________________
* |PB4(SSI2CLK)---->SCLK|
* |PB5(SSI2FSS)---->CSB |
* |PB6(SSI2RX) <---- SDO|
* Tiva M4|PB7(SSI2TX)---->SDI|LDC1000
* |PB0(Timer CLK)----> TBCLK|
* _____________||______________
*********************************************************************/
void SPIInit()
{
//配置PB6為SSI2RX,即對Tiva M4而言的SPI資料接收線
GPIOPinTypeSSI(GPIO_PORTB_BASE,GPIO_PIN_6) ;
GPIOPinConfigure(GPIO_PB6_SSI2RX);
//配置PB6為SSI2TX,即對Tiva M4而言的SPI資料傳送線
GPIOPinTypeSSI(GPIO_PORTB_BASE,GPIO_PIN_7) ;
GPIOPinConfigure(GPIO_PB7_SSI2TX);
//配置PB4為SSI2CLK線,作為時鐘線
GPIOPinTypeSSI(GPIO_PORTB_BASE,GPIO_PIN_4) ;
GPIOPinConfigure(GPIO_PB4_SSI2CLK);
//配置PB5為SSI2FFS線,作為片選線
GPIOPinTypeSSI(GPIO_PORTB_BASE,GPIO_PIN_5) ;
GPIOPinConfigure(GPIO_PB5_SSI2FSS);
SSIDisable(SSI2_BASE); //禁能SSI2
//配置SSI2為SSI_FRF_MOTO_MODE_0協議格式,SPI主模式,時鐘源為5K,16位資料長度
SSIConfigSetExpClk(SSI2_BASE,SysCtlClockGet(),SSI_FRF_MOTO_MODE_0,SSI_MODE_MASTER,5000,16);
SSIEnable(SSI2_BASE) ;//使能SSI2
}
/********************************************************************
*@brief: PA4中斷服務函式,該函式在startup_ccs.c的中斷向量表中進行
*了註冊
*@param: none
*@return: none
* ____________________
* ||
* ||
* Tiva M4 | PA4 <--- INTB| LDC1000
* ||
* —————————||__________
*********************************************************************/
void GPIOAIntHandler()
{
GPIOIntClear(GPIO_PORTA_BASE,GPIO_INT_PIN_4) ;//清除PA4中斷標誌
DataRdy = 1 ; //LDC1000中斷標誌置位
GPIOIntDisable(GPIO_PORTA_BASE,GPIO_INT_PIN_4) ;//禁能PA4中斷,將在SPI資料讀取完成後重新使能PA4中斷
}
//---------------------------------------main函式----------------------------------------------
int main(void)
{
SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ |//配置主時鐘為50MHz
SYSCTL_OSC_MAIN);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA) ;//使能GPIOA外設模組
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB) ; //使能GPIOB外設模組
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER2); //使能Timer2外設模組
SysCtlPeripheralEnable(SYSCTL_PERIPH_SSI2); //使能SSI2外設模組
DataRdy = 0 ;//LDC1000中斷標誌清零
TimerInit() ;//TBCLK所在時鐘初始化
GPIOInit() ;//GPIO初始化(PA4)
SPIInit() ;//SPI初始化
LDC1000_init();//LDC1000初始配置
ROM_IntMasterEnable(); //使能總中斷
while(HWREG(SSI2_BASE + SSI_O_SR)& SSI_SR_RNE) //首先清除SPI上的接收快取,排除干擾
{
DataRcv[0] = HWREG(SSI2_BASE + SSI_O_DR) ;
}
while(1)
{
//轉化結束後讀取ProximityData和FrequencyData
if(DataRdy)
{
DataRdy = 0 ;//LDC1000中斷標誌清零(在PA4中斷服務程式中置位)
LDCRead() ;//SPI讀取資料操作
}
}
}
怎樣在stm32中寫這個程式????
大家一起討論