本文将会简单的介绍如何使用STM32F103ZE驱动PMW3901光流模块,使用标准库。
所用材料如下 一块 STM32F103最小系统板以及一个 PMW3901光流模块
通过查阅PMW3901的数据手册可以得知,该芯片采用SPI通信,模块上面已经标注了SPI通信用的引脚。
所以首先,我们要先实现STM32F103的SPI通信。这里我们先写SPI通信要用到的头文件,SPI.h
#ifndef __SPI_PMW_H
#define __SPI_PMW_H
#include "stm32f10x.h"
#include <stdio.h>
/*等待超时时间*/
#define SPIT_FLAG_TIMEOUT ((uint32_t)0x1000)
#define SPIT_LONG_TIMEOUT ((uint32_t)(10 * SPIT_FLAG_TIMEOUT))
/*SPI接口定义-开头****************************/
#define PMW_SPIx SPI1
#define PMW_SPI_APBxClock_FUN RCC_APB2PeriphClockCmd
#define PMW_SPI_CLK RCC_APB2Periph_SPI1
//CS(NSS)引脚 片选选普通GPIO即可
#define PMW_SPI_CS_APBxClock_FUN RCC_APB2PeriphClockCmd
#define PMW_SPI_CS_CLK RCC_APB2Periph_GPIOC
#define PMW_SPI_CS_PORT GPIOA
#define PMW_SPI_CS_PIN GPIO_Pin_12
//SCK引脚
#define PMW_SPI_SCK_APBxClock_FUN RCC_APB2PeriphClockCmd
#define PMW_SPI_SCK_CLK RCC_APB2Periph_GPIOA
#define PMW_SPI_SCK_PORT GPIOA
#define PMW_SPI_SCK_PIN GPIO_Pin_5
//MISO引脚
#define PMW_SPI_MISO_APBxClock_FUN RCC_APB2PeriphClockCmd
#define PMW_SPI_MISO_CLK RCC_APB2Periph_GPIOA
#define PMW_SPI_MISO_PORT GPIOA
#define PMW_SPI_MISO_PIN GPIO_Pin_6
//MOSI引脚
#define PMW_SPI_MOSI_APBxClock_FUN RCC_APB2PeriphClockCmd
#define PMW_SPI_MOSI_CLK RCC_APB2Periph_GPIOA
#define PMW_SPI_MOSI_PORT GPIOA
#define PMW_SPI_MOSI_PIN GPIO_Pin_7
#define SPI_PMW_CS_LOW() GPIO_ResetBits( PMW_SPI_CS_PORT, PMW_SPI_CS_PIN )
#define SPI_PMW_CS_HIGH() GPIO_SetBits( PMW_SPI_CS_PORT, PMW_SPI_CS_PIN )
/*SPI接口定义-结尾****************************/
void SPI_PMW_Init(void);
#define PMW_ERROR(fmt,arg...) printf("<<-PMW-ERROR->> "fmt"\n",##arg)
uint8_t SPI_PMW_ReadByte(u8 byte);
u8 SPI_PMW_SendByte(u8 location ,u8 byte );
#endif /* __SPI_PMW_H */
然后再写spi实现的C语言文件
#include "spi.h"
uint16_t test;
static __IO uint32_t SPITimeout = SPIT_LONG_TIMEOUT;
static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode);
void SPI_PMW_Init(void)
{
SPI_InitTypeDef SPI_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
/* 使能SPI时钟 */
PMW_SPI_APBxClock_FUN ( PMW_SPI_CLK, ENABLE );
/* 使能SPI引脚相关的时钟 */
PMW_SPI_CS_APBxClock_FUN ( PMW_SPI_CS_CLK|PMW_SPI_SCK_CLK|
PMW_SPI_MISO_PIN|PMW_SPI_MOSI_PIN, ENABLE );
/* 配置SPI的 CS引脚,普通IO即可 */
GPIO_InitStructure.GPIO_Pin = PMW_SPI_CS_PIN;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_Init(PMW_SPI_CS_PORT, &GPIO_InitStructure);
/* 配置SPI的 SCK引脚*/
GPIO_InitStructure.GPIO_Pin = PMW_SPI_SCK_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_Init(PMW_SPI_SCK_PORT, &GPIO_InitStructure);
/* 配置SPI的 MISO引脚*/
GPIO_InitStructure.GPIO_Pin = PMW_SPI_MISO_PIN;
GPIO_Init(PMW_SPI_MISO_PORT, &GPIO_InitStructure);
/* 配置SPI的 MOSI引脚*/
GPIO_InitStructure.GPIO_Pin = PMW_SPI_MOSI_PIN;
GPIO_Init(PMW_SPI_MOSI_PORT, &GPIO_InitStructure);
/* 停止信号 PMW: CS引脚高电平*/
SPI_PMW_CS_HIGH();
/* SPI 模式配置 */
// PMW芯片 支持SPI模式0及模式3,据此设置CPOL CPHA
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High;
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge;
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft;
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_4;
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
SPI_InitStructure.SPI_CRCPolynomial = 7;
SPI_Init(PMW_SPIx , &SPI_InitStructure);
/* 使能 SPI */
SPI_Cmd(PMW_SPIx , ENABLE);
}
u8 SPI_PMW_SendByte(uint8_t location ,uint8_t byte )
{
location |= 0x80u;
SPI_PMW_CS_LOW();
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待发送缓冲区为空,TXE事件 */
while (SPI_I2S_GetFlagStatus(PMW_SPIx , SPI_I2S_FLAG_TXE) == RESET)
{
if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);
}
/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
SPI_I2S_SendData(PMW_SPIx , location);
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待接收缓冲区非空,RXNE事件 */
while (SPI_I2S_GetFlagStatus(PMW_SPIx , SPI_I2S_FLAG_RXNE) == RESET)
{
if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);
}
SPI_I2S_ReceiveData(PMW_SPIx );
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待发送缓冲区为空,TXE事件 */
while (SPI_I2S_GetFlagStatus(PMW_SPIx , SPI_I2S_FLAG_TXE) == RESET)
{
if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);
}
/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
SPI_I2S_SendData(PMW_SPIx , byte);
SPI_PMW_CS_HIGH();
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待接收缓冲区非空,RXNE事件 */
return 0;
}
uint8_t SPI_PMW_ReadByte(uint8_t byte)
{
byte &= ~0x80u;
SPI_PMW_CS_LOW();
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待发送缓冲区为空,TXE事件 */
while (SPI_I2S_GetFlagStatus(PMW_SPIx , SPI_I2S_FLAG_TXE) == RESET)
{
if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);
}
/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
SPI_I2S_SendData(PMW_SPIx , byte);
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待接收缓冲区非空,RXNE事件 */
while (SPI_I2S_GetFlagStatus(PMW_SPIx , SPI_I2S_FLAG_RXNE) == RESET)
{
if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);
}
test = SPI_I2S_ReceiveData(PMW_SPIx );
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待发送缓冲区为空,TXE事件 */
while (SPI_I2S_GetFlagStatus(PMW_SPIx , SPI_I2S_FLAG_TXE) == RESET)
{
if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(0);
}
/* 写入数据寄存器,把要写入的数据写入发送缓冲区 */
SPI_I2S_SendData(PMW_SPIx , 0);
SPITimeout = SPIT_FLAG_TIMEOUT;
/* 等待接收缓冲区非空,RXNE事件 */
while (SPI_I2S_GetFlagStatus(PMW_SPIx , SPI_I2S_FLAG_RXNE) == RESET)
{
if((SPITimeout--) == 0) return SPI_TIMEOUT_UserCallback(1);
}
test = SPI_I2S_ReceiveData(PMW_SPIx );
SPI_PMW_CS_HIGH();
/* 读取数据寄存器,获取接收缓冲区数据 */
return test ;
}
static uint16_t SPI_TIMEOUT_UserCallback(uint8_t errorCode)
{
/* 等待超时后的处理,输出错误信息 */
PMW_ERROR("SPI 等待超时!errorCode = %d",errorCode);
return 0;
}
这样在主函数中,我们可以通过SPI_PMW_ReadByte和SPI_PMW_SendByte来读取或者传输数据给光流模块
然后查阅芯片手册,得知芯片在上电的时候要进行初始化。
并且,通过读取地址为0x5F寄存器中的数据,如果返回值是0xB6则说明SPI正常工作
所以main.c中初始化函数如下。
void initRegisters()
{
SPI_PMW_SendByte(0x7F, 0x00);
SPI_PMW_SendByte(0x61, 0xAD);
SPI_PMW_SendByte(0x7F, 0x03);
SPI_PMW_SendByte(0x40, 0x00);
SPI_PMW_SendByte(0x7F, 0x05);
SPI_PMW_SendByte(0x41, 0xB3);
SPI_PMW_SendByte(0x43, 0xF1);
SPI_PMW_SendByte(0x45, 0x14);
SPI_PMW_SendByte(0x5B, 0x32);
SPI_PMW_SendByte(0x5F, 0x34);
SPI_PMW_SendByte(0x7B, 0x08);
SPI_PMW_SendByte(0x7F, 0x06);
SPI_PMW_SendByte(0x44, 0x1B);
SPI_PMW_SendByte(0x40, 0xBF);
SPI_PMW_SendByte(0x4E, 0x3F);
SPI_PMW_SendByte(0x7F, 0x08);
SPI_PMW_SendByte(0x65, 0x20);
SPI_PMW_SendByte(0x6A, 0x18);
SPI_PMW_SendByte(0x7F, 0x09);
SPI_PMW_SendByte(0x4F, 0xAF);
SPI_PMW_SendByte(0x5F, 0x40);
SPI_PMW_SendByte(0x48, 0x80);
SPI_PMW_SendByte(0x49, 0x80);
SPI_PMW_SendByte(0x57, 0x77);
SPI_PMW_SendByte(0x60, 0x78);
SPI_PMW_SendByte(0x61, 0x78);
SPI_PMW_SendByte(0x62, 0x08);
SPI_PMW_SendByte(0x63, 0x50);
SPI_PMW_SendByte(0x7F, 0x0A);
SPI_PMW_SendByte(0x45, 0x60);
SPI_PMW_SendByte(0x7F, 0x00);
SPI_PMW_SendByte(0x4D, 0x11);
SPI_PMW_SendByte(0x55, 0x80);
SPI_PMW_SendByte(0x74, 0x1F);
SPI_PMW_SendByte(0x75, 0x1F);
SPI_PMW_SendByte(0x4A, 0x78);
SPI_PMW_SendByte(0x4B, 0x78);
SPI_PMW_SendByte(0x44, 0x08);
SPI_PMW_SendByte(0x45, 0x50);
SPI_PMW_SendByte(0x64, 0xFF);
SPI_PMW_SendByte(0x65, 0x1F);
SPI_PMW_SendByte(0x7F, 0x14);
SPI_PMW_SendByte(0x65, 0x60);
SPI_PMW_SendByte(0x66, 0x08);
SPI_PMW_SendByte(0x63, 0x78);
SPI_PMW_SendByte(0x7F, 0x15);
SPI_PMW_SendByte(0x48, 0x58);
SPI_PMW_SendByte(0x7F, 0x07);
SPI_PMW_SendByte(0x41, 0x0D);
SPI_PMW_SendByte(0x43, 0x14);
SPI_PMW_SendByte(0x4B, 0x0E);
SPI_PMW_SendByte(0x45, 0x0F);
SPI_PMW_SendByte(0x44, 0x42);
SPI_PMW_SendByte(0x4C, 0x80);
SPI_PMW_SendByte(0x7F, 0x10);
SPI_PMW_SendByte(0x5B, 0x02);
SPI_PMW_SendByte(0x7F, 0x07);
SPI_PMW_SendByte(0x40, 0x41);
SPI_PMW_SendByte(0x70, 0x00);
delay_ms(100);
SPI_PMW_SendByte(0x32, 0x44);
SPI_PMW_SendByte(0x7F, 0x07);
SPI_PMW_SendByte(0x40, 0x40);
SPI_PMW_SendByte(0x7F, 0x06);
SPI_PMW_SendByte(0x62, 0xf0);
SPI_PMW_SendByte(0x63, 0x00);
SPI_PMW_SendByte(0x7F, 0x0D);
SPI_PMW_SendByte(0x48, 0xC0);
SPI_PMW_SendByte(0x6F, 0xd5);
SPI_PMW_SendByte(0x7F, 0x00);
SPI_PMW_SendByte(0x5B, 0xa0);
SPI_PMW_SendByte(0x4E, 0xA8);
SPI_PMW_SendByte(0x5A, 0x50);
SPI_PMW_SendByte(0x40, 0x80);
}
void PMWinit() {
SPI_PMW_CS_HIGH();
delay_ms(100);
SPI_PMW_CS_LOW();
delay_ms(100);
SPI_PMW_SendByte(0x3A,0x5A);
delay_ms(10);
chipId = SPI_PMW_ReadByte(0x00);
delay_ms(30);
dIpihc = SPI_PMW_ReadByte(0x5F);
SPI_PMW_ReadByte(0x02);
SPI_PMW_ReadByte(0x03);
SPI_PMW_ReadByte(0x04);
SPI_PMW_ReadByte(0x05);
SPI_PMW_ReadByte(0x06);
delay_ms(1000);
initRegisters();
}
接下来,按照数据手册寄存器0x04和0x03分别代表了X轴偏移量的高8位和低8位,而0x06和0x05则是Y轴的。
通过该函数,可以读取值
void readMotionCount(int16_t *deltax, int16_t *deltay){
SPI_PMW_ReadByte(0x02);
*deltax = ((int16_t)SPI_PMW_ReadByte(0x04) << 8) | SPI_PMW_ReadByte(0x03);
*deltay = ((int16_t)SPI_PMW_ReadByte(0x06) << 8) | SPI_PMW_ReadByte(0x05);
}
接下来将模块和单片机进行SPI通信的连接,如图 MOSI -> PA7 MISO->PA6 CLK->PA5 CS-> PA12
然后,我们可以通过main.c代码如下
#include "stm32f10x.h"
#include "spi.h"
int16_t deltax = 0 ;
int16_t deltay = 0 ;
uint8_t a ;
/* 获取缓冲区的长度 */
void delay_ms(u16 time);
void readMotionCount(int16_t *deltax, int16_t *deltay){
SPI_PMW_ReadByte(0x02);
*deltax = ((int16_t)SPI_PMW_ReadByte(0x04) << 8) | SPI_PMW_ReadByte(0x03);
*deltay = ((int16_t)SPI_PMW_ReadByte(0x06) << 8) | SPI_PMW_ReadByte(0x05);
}
int timer = 1 ;
int init = 0 ;
int dx, dy ;
void PMWinit(void);
int Data[50] ;
int i = 0;
uint16_t chipId ;
uint16_t dIpihc ;
int16_t deltax,deltay;
int main(void)
{
SPI_PMW_Init();
//chipId = SPI_ReadData(0x00);
delay_ms(100);
PMWinit();
delay_ms(100);
while(1) {
readMotionCount(&deltax,&deltay);
if(deltax >= -32640 && deltax <-30000 ){
deltax = deltax + 32640;
}
if(deltay >= -32640 && deltay <-30000 ){
deltay = deltay + 32640;
}
delay_ms(100);
}
}
void delay_ms(u16 time)
{
u16 i=0;
while(time--)
{
i=12000; //????
while(i--) ;
}
}
void initRegisters()
{
SPI_PMW_SendByte(0x7F, 0x00);
SPI_PMW_SendByte(0x61, 0xAD);
SPI_PMW_SendByte(0x7F, 0x03);
SPI_PMW_SendByte(0x40, 0x00);
SPI_PMW_SendByte(0x7F, 0x05);
SPI_PMW_SendByte(0x41, 0xB3);
SPI_PMW_SendByte(0x43, 0xF1);
SPI_PMW_SendByte(0x45, 0x14);
SPI_PMW_SendByte(0x5B, 0x32);
SPI_PMW_SendByte(0x5F, 0x34);
SPI_PMW_SendByte(0x7B, 0x08);
SPI_PMW_SendByte(0x7F, 0x06);
SPI_PMW_SendByte(0x44, 0x1B);
SPI_PMW_SendByte(0x40, 0xBF);
SPI_PMW_SendByte(0x4E, 0x3F);
SPI_PMW_SendByte(0x7F, 0x08);
SPI_PMW_SendByte(0x65, 0x20);
SPI_PMW_SendByte(0x6A, 0x18);
SPI_PMW_SendByte(0x7F, 0x09);
SPI_PMW_SendByte(0x4F, 0xAF);
SPI_PMW_SendByte(0x5F, 0x40);
SPI_PMW_SendByte(0x48, 0x80);
SPI_PMW_SendByte(0x49, 0x80);
SPI_PMW_SendByte(0x57, 0x77);
SPI_PMW_SendByte(0x60, 0x78);
SPI_PMW_SendByte(0x61, 0x78);
SPI_PMW_SendByte(0x62, 0x08);
SPI_PMW_SendByte(0x63, 0x50);
SPI_PMW_SendByte(0x7F, 0x0A);
SPI_PMW_SendByte(0x45, 0x60);
SPI_PMW_SendByte(0x7F, 0x00);
SPI_PMW_SendByte(0x4D, 0x11);
SPI_PMW_SendByte(0x55, 0x80);
SPI_PMW_SendByte(0x74, 0x1F);
SPI_PMW_SendByte(0x75, 0x1F);
SPI_PMW_SendByte(0x4A, 0x78);
SPI_PMW_SendByte(0x4B, 0x78);
SPI_PMW_SendByte(0x44, 0x08);
SPI_PMW_SendByte(0x45, 0x50);
SPI_PMW_SendByte(0x64, 0xFF);
SPI_PMW_SendByte(0x65, 0x1F);
SPI_PMW_SendByte(0x7F, 0x14);
SPI_PMW_SendByte(0x65, 0x60);
SPI_PMW_SendByte(0x66, 0x08);
SPI_PMW_SendByte(0x63, 0x78);
SPI_PMW_SendByte(0x7F, 0x15);
SPI_PMW_SendByte(0x48, 0x58);
SPI_PMW_SendByte(0x7F, 0x07);
SPI_PMW_SendByte(0x41, 0x0D);
SPI_PMW_SendByte(0x43, 0x14);
SPI_PMW_SendByte(0x4B, 0x0E);
SPI_PMW_SendByte(0x45, 0x0F);
SPI_PMW_SendByte(0x44, 0x42);
SPI_PMW_SendByte(0x4C, 0x80);
SPI_PMW_SendByte(0x7F, 0x10);
SPI_PMW_SendByte(0x5B, 0x02);
SPI_PMW_SendByte(0x7F, 0x07);
SPI_PMW_SendByte(0x40, 0x41);
SPI_PMW_SendByte(0x70, 0x00);
delay_ms(100);
SPI_PMW_SendByte(0x32, 0x44);
SPI_PMW_SendByte(0x7F, 0x07);
SPI_PMW_SendByte(0x40, 0x40);
SPI_PMW_SendByte(0x7F, 0x06);
SPI_PMW_SendByte(0x62, 0xf0);
SPI_PMW_SendByte(0x63, 0x00);
SPI_PMW_SendByte(0x7F, 0x0D);
SPI_PMW_SendByte(0x48, 0xC0);
SPI_PMW_SendByte(0x6F, 0xd5);
SPI_PMW_SendByte(0x7F, 0x00);
SPI_PMW_SendByte(0x5B, 0xa0);
SPI_PMW_SendByte(0x4E, 0xA8);
SPI_PMW_SendByte(0x5A, 0x50);
SPI_PMW_SendByte(0x40, 0x80);
}
void PMWinit() {
SPI_PMW_CS_HIGH();
delay_ms(100);
SPI_PMW_CS_LOW();
delay_ms(100);
SPI_PMW_SendByte(0x3A,0x5A);
delay_ms(10);
chipId = SPI_PMW_ReadByte(0x00);
delay_ms(30);
dIpihc = SPI_PMW_ReadByte(0x5F);
SPI_PMW_ReadByte(0x02);
SPI_PMW_ReadByte(0x03);
SPI_PMW_ReadByte(0x04);
SPI_PMW_ReadByte(0x05);
SPI_PMW_ReadByte(0x06);
delay_ms(1000);
initRegisters();
}
我们可以通过keil自带的debug来确认spi通信是否成功
如图,左下角变量dlpihc显示0x00B6这里是32位,16位即0xB6 ,通信成功
我们可以通过串口通信来验证模块是否能正常返回数据。
此处串口通信代码省略,实际结果如下。
实际效果受环境光影响较大,后续可以考虑加滤波。