STM32固件库对SPI的支持相当得好,但要理解以下几点,不然对使用不当。
当时写了一个CMA3000的驱动程序,主要是使用SPI接口完成一些相应的配置操作,然后读出数据。
但在调试的时候遇到了一些困难,程序读出的数据一直是0x3A,无论读取什么寄存器也是这一个值。
当时郁闷了好久,接上示波器观察波形,发出0x3A是在MCU向CMA3000发送地址时MISO上的值。
并且有一个很重要的发现:SCK只有8个clk,这是不应该的,因为在发送完地址后,还有一次读取的过程,
按我的理解应该还有一次8个clk出现,这样才能读出数据,但后来发出,只有在MOSI写入的时候才会有SCK,
也就是说,如果数据是在addr被写入后的下一次SCK出现,那么应该再写一次!
只有写入时才会有SCK!
找到这个问题之后,所有的问题都解决了。
另外SPI还有好几种模式,主要是在idle时SCK是高电平还是低电平,还有就是上升沿触发还是下降沿触发。
具体用哪一种模式要根据器件的手册来定。
具体的代码如下:
#ifndef __CMA3000_H__ #define __CMA3000_H__ #include "HKY_timer.h" void CMA3000_init(void); u8 CMA3000_getMotionState(void); u8 CMA3000_read(u8 addr); u8 CMA3000_write(u8 addr, u8 cmd); u8 CMA3000_getXYZ(u8 *px, u8 *py, u8 *pz); #endif // __CMA3000_H__
#include "HKY_cma3000.h" #define CMA_SPI SPI1 #define CMA_APB_SPI RCC_APB2Periph_SPI1 #define CMA_PIN_PORT GPIOA #define CMA_PIN_NSS GPIO_Pin_4 #define CMA_PIN_SCK GPIO_Pin_5 #define CMA_PIN_MISO GPIO_Pin_6 #define CMA_PIN_MOSI GPIO_Pin_7 #define CMA3000_CS_LOW() GPIO_ResetBits(CMA_PIN_PORT, CMA_PIN_NSS) #define CMA3000_CS_HIGH() GPIO_SetBits(CMA_PIN_PORT, CMA_PIN_NSS) // CMA3000 寄存器 #define WHO_AM_I 0x00 #define REVID 0x01 #define CTRL 0x02 #define STATUS 0x03 #define RSTR 0x04 #define INT_STATUS 0x05 #define DOUTX 0x06 #define DOUTY 0x07 #define DOUTZ 0x08 #define MDTHR 0x09 #define MDFFTMR 0x0A #define FFTHR 0x0B #define I2C_ADDR 0x0C /* Control Register setup */ // G_RANGE #define G_RANGE_2 0x80 // 2g range // INT_LEVEL #define INT_LEVEL_LOW 0x40 // INT active high // MDET_EXIT #define MDET_NO_EXIT 0x20 // Remain in motion detection mode // I2C_DIS #define I2C_DIS 0x10 // I2C disabled // MODE[2:0] #define MODE_PD 0x00 // Power Down #define MODE_100 0x02 // Measurement mode 100 Hz ODR #define MODE_400 0x04 // Measurement mode 400 Hz ODR #define MODE_40 0x06 // Measurement mode 40 Hz ODR #define MODE_MD_10 0x08 // Motion detection mode 10 Hz ODR #define MODE_FF_100 0x0A // Free fall detection mode 100 Hz ODR #define MODE_FF_400 0x0C // Free fall detection mode 400 Hz ODR // INT_DIS #define INT_DIS 0x01 // Interrupts enabled extern void HKY_delayUs(vu32 m); extern void HKY_delayMs(vu32 m); static void CMA3000_SPI_init() { GPIO_InitTypeDef GPIO_InitStructure; SPI_InitTypeDef SPI_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA , ENABLE ); // configure SCK, MISO, MOSI pins GPIO_InitStructure.GPIO_Pin = CMA_PIN_SCK | CMA_PIN_MISO | CMA_PIN_MOSI; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; /*GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD;*/ GPIO_Init(CMA_PIN_PORT, &GPIO_InitStructure); // configure NSS pin GPIO_InitStructure.GPIO_Pin = CMA_PIN_NSS; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(CMA_PIN_PORT, &GPIO_InitStructure); //SPI port initialization RCC_APB2PeriphClockCmd(CMA_APB_SPI, ENABLE); SPI_Cmd(CMA_SPI, DISABLE); SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; SPI_InitStructure.SPI_Mode = SPI_Mode_Master;//SPI_Mode_Slave; SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low; // SCK is low when idle SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge; //rising edge SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; SPI_Init(CMA_SPI, &SPI_InitStructure); SPI_Cmd(CMA_SPI, ENABLE); } u8 CMA3000_write(u8 addr, u8 cmd) { u8 ret; addr = (addr << 2) | 0x02; CMA3000_CS_LOW(); HKY_delayUs(10); // read to clear rx flag ret = SPI_I2S_ReceiveData(CMA_SPI); // send address while((SPI_I2S_GetFlagStatus(CMA_SPI, SPI_I2S_FLAG_TXE))==RESET); SPI_I2S_SendData(CMA_SPI, addr); // wait while( (SPI_I2S_GetFlagStatus(CMA_SPI, SPI_I2S_FLAG_RXNE)) == RESET ); ret = SPI_I2S_ReceiveData(CMA_SPI); // send command while((SPI_I2S_GetFlagStatus(CMA_SPI, SPI_I2S_FLAG_TXE))==RESET); SPI_I2S_SendData(CMA_SPI, cmd); // wait while( (SPI_I2S_GetFlagStatus(CMA_SPI, SPI_I2S_FLAG_RXNE)) == RESET ); ret = SPI_I2S_ReceiveData(CMA_SPI); HKY_delayUs(10); CMA3000_CS_HIGH(); return ret; } u8 CMA3000_read(u8 addr) { u8 ret; addr = (addr << 2); CMA3000_CS_LOW(); HKY_delayUs(10); // clear rx flag ret = SPI_I2S_ReceiveData(CMA_SPI); // send address while((SPI_I2S_GetFlagStatus(CMA_SPI, SPI_I2S_FLAG_TXE))==RESET); SPI_I2S_SendData(CMA_SPI, addr); // wait while( (SPI_I2S_GetFlagStatus(CMA_SPI, SPI_I2S_FLAG_RXNE)) == RESET ); ret = SPI_I2S_ReceiveData(CMA_SPI); // dummy write while((SPI_I2S_GetFlagStatus(CMA_SPI, SPI_I2S_FLAG_TXE))==RESET); SPI_I2S_SendData(CMA_SPI, 0); // read while( (SPI_I2S_GetFlagStatus(CMA_SPI, SPI_I2S_FLAG_RXNE)) == RESET ); ret = SPI_I2S_ReceiveData(CMA_SPI); HKY_delayUs(10); CMA3000_CS_HIGH(); return ret; } u8 CMA3000_getMotionState() { u8 ret = 0x0; ret = CMA3000_read(INT_STATUS); return ( (ret & 0x03) != 0); } void CMA3000_reset() { // writing 0x02, 0x0A, 0x04 to reset ASIC CMA3000_write(RSTR, 0x02); HKY_delayUs(50); CMA3000_write(RSTR, 0x0A); HKY_delayUs(50); CMA3000_write(RSTR, 0x04); HKY_delayUs(50); } void CMA3000_init() { CMA3000_SPI_init(); CMA3000_reset(); HKY_delayMs(100); CMA3000_write(MDTHR, 0x02); //143 mg HKY_delayUs(50); CMA3000_write(MDFFTMR, 0x10); //100 ms HKY_delayUs(50); /*CMA3000_write(CTRL, 0x38); */ CMA3000_write(CTRL, MDET_NO_EXIT | I2C_DIS | MODE_MD_10); HKY_delayUs(50); HKY_delayMs(50); CMA3000_getMotionState(); HKY_delayMs(50); CMA3000_getMotionState(); HKY_delayMs(50); CMA3000_getMotionState(); } u8 CMA3000_getXYZ(u8 *px, u8 *py, u8 *pz) { *px = CMA3000_read(DOUTX); HKY_delayUs(50); if (*px == 0xff) return -1; *py = CMA3000_read(DOUTY); HKY_delayUs(50); if (*py == 0xff) return -1; *pz = CMA3000_read(DOUTZ); HKY_delayUs(50); if (*pz == 0xff) return -1; return 0; }