目录
1.SPI的配置方式
SPI**简介**
在大容量产品和互联型产品上,SPI接口可以配置为支持SPI协议或者支持I2S音频协议。SPI接
口默认工作在SPI方式,可以通过软件把功能从SPI模式切换到I2S模式。
在小容量和中容量产品上,不支持I2S音频协议。
SPI**特征**
● 3线全双工同步传输
● 带或不带第三根双向数据线的双线单工同步传输
● 8或16位传输帧格式选择
● 主或从操作
● 支持多主模式
● 8个主模式波特率预分频系数(最大为fPCLK/2)
● 从模式频率 (最大为fPCLK/2)
● 主模式和从模式的快速通信
● 主模式和从模式下均可以由软件或硬件进行NSS管理:主/从操作模式的动态改变
● 可编程的时钟极性和相位
● 可编程的数据顺序,MSB在前或LSB在前
● 可触发中断的专用发送和接收标志
● SPI总线忙状态标志
● 支持可靠通信的硬件CRC
─ 在发送模式下,CRC值可以被作为最后一个字节发送
SPI接口
─ 在全双工模式中对接收到的最后一个字节自动进行CRC校验。
● 可触发中断的主模式故障、过载以及CRC错误标志。
● 支持DMA功能的1字节发送和接收缓冲器:产生发送和接受请求。
SPI接口 STM32F10xxx参考手册
─ 在全双工模式中对接收到的最后一个字节自动进行CRC校验
SPI**功能描述**
通常SPI通过4个引脚与外部器件相连:
● MISO:主设备输入/从设备输出引脚。该引脚在从模式下发送数据,在主模式下接收数据。
● MOSI:主设备输出/从设备输入引脚。该引脚在主模式下发送数据,在从模式下接收数据。
● SCK:串口时钟,作为主设备的输出,从设备的输入。
● NSS:从设备选择。这是一个可选的引脚,用来选择主/从设备。它的功能是用来作为“片
选引脚”,让主设备可以单独地与特定从设备通讯,避免数据线上的冲突。从设备的NSS
引脚可以由主设备的一个标准I/O引脚来驱动。一旦被使能(SSOE位),NSS引脚也可以作为
输出引脚,并在SPI处于主模式时拉低;此时,所有的SPI设备,如果它们的NSS引脚连接
到主设备的NSS引脚,则会检测到低电平,如果它们被设置为NSS硬件模式,就会自动进
入从设备状态。当配置为主设备、NSS配置为输入引脚(MSTR=1,SSOE=0)时,如果NSS
被拉低,则这个SPI设备进入主模式失败状态:即MSTR位被自动清除,此设备进入从模式。
SPI通信配置
1)配置相关引脚的复用功能,使能 SPI2 时钟, IO 初始化 2)初始化 SPI2,设置 SPI2 工作模式
void SPI_Init(SPI_TypeDef* SPIx, PI_InitTypeDef* SPI_InitStruct);
3)使能 SPI2
SPI_Cmd(SPI2, ENABLE); //使能 SPI 外设
4)SPI 传输数据
void SPI_I2S_SendData(SPI_TypeDef* SPIx, uint16_t Data);
5)查看 SPI 传输状态
SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE);
//以下是SPI模块的初始化代码,配置成主机模式
//SPI口初始化
//这里针是对SPI2的初始化
void SPI2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
SPI_InitTypeDef SPI_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE );//PORTB时钟使能
RCC_APB1PeriphClockCmd( RCC_APB1Periph_SPI2, ENABLE );//SPI2时钟使能
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //PB13/14/15复用推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);//初始化GPIOB
GPIO_SetBits(GPIOB,GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15); //PB13/14/15上拉
SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex; //设置SPI单向或者双向的数据模式:SPI设置为双线双向全双工
SPI_InitStructure.SPI_Mode = SPI_Mode_Master; //设置SPI工作模式:设置为主SPI
SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b; //设置SPI的数据大小:SPI发送接收8位帧结构
SPI_InitStructure.SPI_CPOL = SPI_CPOL_High; //串行同步时钟的空闲状态为高电平
SPI_InitStructure.SPI_CPHA = SPI_CPHA_2Edge; //串行同步时钟的第二个跳变沿(上升或下降)数据被采样
SPI_InitStructure.SPI_NSS = SPI_NSS_Soft; //NSS信号由硬件(NSS管脚)还是软件(使用SSI位)管理:内部NSS信号有SSI位控制
SPI_InitStructure.SPI_BaudRatePrescaler = SPI_BaudRatePrescaler_256; //定义波特率预分频的值:波特率预分频值为256
SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始
SPI_InitStructure.SPI_CRCPolynomial = 7; //CRC值计算的多项式
SPI_Init(SPI2, &SPI_InitStructure); //根据SPI_InitStruct中指定的参数初始化外设SPIx寄存器
SPI_Cmd(SPI2, ENABLE); //使能SPI外设
SPI2_ReadWriteByte(0xff);//启动传输
}
//SPI 速度设置函数
//SpeedSet:
//SPI_BaudRatePrescaler_2 2分频
//SPI_BaudRatePrescaler_8 8分频
//SPI_BaudRatePrescaler_16 16分频
//SPI_BaudRatePrescaler_256 256分频
void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler)
{
assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));
SPI2->CR1&=0XFFC7;
SPI2->CR1|=SPI_BaudRatePrescaler; //设置SPI2速度
SPI_Cmd(SPI2,ENABLE);
}
//SPIx 读写一个字节
//TxData:要写入的字节
//返回值:读取到的字节
u8 SPI2_ReadWriteByte(u8 TxData)
{
u8 retry=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_TXE) == RESET) //检查指定的SPI标志位设置与否:发送缓存空标志位
{
retry++;
if(retry>200)return 0;
}
SPI_I2S_SendData(SPI2, TxData); //通过外设SPIx发送一个数据
retry=0;
while (SPI_I2S_GetFlagStatus(SPI2, SPI_I2S_FLAG_RXNE) == RESET) //检查指定的SPI标志位设置与否:接受缓存非空标志位
{
retry++;
if(retry>200)return 0;
}
return SPI_I2S_ReceiveData(SPI2); //返回通过SPIx最近接收的数据
}
2.IIC 的配置方式
IIC简介
I2 C(芯片间)总线接口连接微控制器和串行I2C总线。它提供多主机功能,控制所有I2C总线特定的时序、协议、仲裁和定时。支持标准和快速两种模式,同时与SMBus 2.0兼容。
I2C模块有多种用途,包括CRC码的生成和校验、SMBus(系统管理总线—System Management
Bus)和PMBus(电源管理总线—Power Management Bus)。
根据特定设备的需要,可以使用DMA以减轻CPU的负担。
I2C主要特点
● 并行总线/I2C总线协议转换器
● 多主机功能:该模块既可做主设备也可做从设备
● I2C主设备功能
─ 产生时钟
─ 产生起始和停止信号
● I2C从设备功能
─ 可编程的I2C地址检测
─ 可响应2个从地址的双地址能力
─ 停止位检测
● 产生和检测7位/10位地址和广播呼叫
● 支持不同的通讯速度
─ 标准速度(高达100 kHz)
─ 快速(高达400 kHz)
● 状态标志:
─ 发送器/接收器模式标志
─ 字节发送结束标志
─ I2C总线忙标志
● 错误标志
─ 主模式时的仲裁丢失
─ 地址/数据传输后的应答(ACK)错误
─ 检测到错位的起始或停止条件
─ 禁止拉长时钟功能时的上溢或下溢
● 2个中断向量
─ 1个中断用于地址/数据通讯成功
─ 1个中断用于错误
● 可选的拉长时钟功能
● 具单字节缓冲器的DMA
● 可配置的PEC(信息包错误检测)的产生或校验:
─ 发送模式中PEC值可以作为最后一个字节传输
─ 用于最后一个接收字节的PEC错误校验
● 兼容SMBus 2.0
─ 25 ms时钟低超时延时
─ 10 ms主设备累积时钟低扩展时间
─ 25 ms从设备累积时钟低扩展时间
─ 带ACK控制的硬件PEC产生/校验
─ 支持地址分辨协议(ARP)
● 兼容SMBus
I2C功能描述
I2C模块接收和发送数据,并将数据从串行转换成并行,或并行转换成串行。可以开启或禁止中
断。接口通过数据引脚(SDA)和时钟引脚(SCL)连接到I2C总线。允许连接到标准(高达100kHz)或
快速(高达400kHz)的I2C总线。
在框架图中我们看到主要有三个引脚,分别是SDA、SCL、SMBA。最主要的通信引脚主要是SDA、SCL引脚。I2C的所有硬件架构都是根据SCL线和 SDA线展开的,其中SMBA我们很少用到SMBA线主要用于SMBUS的.
对I2C系统框架图分解成四部分: ●通讯引脚 ●时钟控制逻辑 ●数据控制逻辑 ●整体控制逻辑
如要细致了解 参考[野火]STM32 HAL库开发实战指南, —基于F103系列开发的手册
I2C 初始化结构体详解
跟其它外设一样,STM32 HAL 库提供了 I2C 初始化结构体及初始化函数来配置 I2C 外设。初始
化结构体及函数定义在库文件“stm32f4xx_hal_i2c.h”及“stm32f4xx_hal_i2c.c”中,编程时我们
可以结合这两个文件内的注释使用或参考库帮助文档。了解初始化结构体后我们就能对 I2C 外
设运用自如了。
typedef struct {
uint32_t ClockSpeed; /*!< 设置 SCL 时钟频率,此值要低于 40 0000*/
uint32_t DutyCycle; /* 指定时钟占空比,可选 low/high = 2:1 及 16:9 模式 */
uint32_t OwnAddress1; /* 指定自身的 I2C 设备地址 1,可以是 7-bit 或者 10-
,
→bit*/
uint32_t AddressingMode; /* 指定地址的长度模式,可以是 7bit 模式或者 10bit 模 式 */
uint32_t DualAddressMode; /* 设置双地址模式 */
uint32_t OwnAddress2; /* 指定自身的 I2C 设备地址 2,只能是 7-bit */
uint32_t GeneralCallMode; /* 指定广播呼叫模式 */
uint32_t NoStretchMode; /* 指定禁止时钟延长模式 */
} I2C_InitTypeDef;
这些结构体成员说明如下,其中括号内的文字是对应参数在 STM32 HAL 库中定义的宏:
(1) ClockSpeed
本成员设置的是 I2C 的传输速率,在调用初始化函数时,函数会根据我们输入的数值写入到 I2C
的时钟控制寄存器 CCR。这个数值的计算上一节已经说明。
(2) DutyCycle
本成员设置的是 I2C 的 SCL 线时钟的占空比。该配置有两个选择,分别为低电平时间比高电平
时间为 2:1 (I2C_DUTYCYCLE_2) 和 16:9 (I2C_DUTYCYCLE_16_9)。其实这两个模式的比例差
别并不大,一般要求都不会如此严格,这里随便选就可以了。
(3) OwnAddress1
本成员配置的是 STM32 的 I2C 设备自身地址 1,每个连接到 I2C 总线上的设备都要有一个自己
的地址,作为主机也不例外。地址可设置为 7 位或 10 位 (受下面 (3) AddressingMode 成员决定),
只要该地址是 I2C 总线上唯一的即可。
STM32 的 I2C 外设可同时使用两个地址,即同时对两个地址作出响应,这个结构成员 OwnAd
dress1 配置的是默认的、OAR1 寄存器存储的地址,若需要设置第二个地址寄存器 OAR2,可使
用 DualAddressMode 成员使能,然后设置 OwnAddress2 成员即可,OAR2 不支持 10 位地址。
(4) AddressingMode
本成员选择 I2C 的寻址模式是 7 位还是 10 位地址。这需要根据实际连接到 I2C 总线上设备的地
址进行选择,这个成员的配置也影响到 OwnAddress1 成员,只有这里设置成 10 位模式时,Ow
nAddress1 才支持 10 位地址。
(5) DualAddressMode
本成员配置的是 STM32 的 I2C 设备自己的地址,每个连接到 I2C 总线上的设备都要有一个自己
的地址,作为主机也不例外。地址可设置为 7 位或 10 位 (受下面 I2C_dual_addressing_mode 成员
决定),只要该地址是 I2C 总线上唯一的即可。
STM32 的 I2C 外设可同时使用两个地址,即同时对两个地址作出响应,这个结构成员
I2C_OwnAddress1 配置的是默认的、OAR1 寄存器存储的地址,若需要设置第二个地址寄存器
OAR2,可使用 I2C_OwnAddress2Confifig 函数来配置,OAR2 不支持 10 位地址。
(6) OwnAddress2
本成员配置的是 STM32 的 I2C 设备自身地址 2,每个连接到 I2C 总线上的设备都要有一个自己
的地址,作为主机也不例外。地址可设置为 7 位,只要该地址是 I2C 总线上唯一的即可。
(7) GeneralCallMode
本成员是关于 I2C 从模式时的广播呼叫模式设置。
(8) NoStretchMode
本成员是关于 I2C 禁止时钟延长模式设置,用于在从模式下禁止时钟延长。它在主模式下必须保持关闭。
配置完这些结构体成员值,调用库函数 HAL_I2C_Init 即可把结构体的配置写入到寄存器中。
I2C的配置 (stm32cubemx)
首先基本的配置在这人就不说了,如若想了解,请移步于作者的其他文章。
1.首先设置RCC;外部晶振8M,STM32F103x的最高主频是72M;
再配置时钟树
2.使能 I2C2,在 Pinout 上选择 I2C2;(默认的PB10,PB11为开漏输出)
如没有其他的特殊要求,其他选项默认就好。自此,stm32cubumx方面的配置工作就完成了。
3.SPI 驱动OLED屏幕
同样 基础配置在这里就不在多说了。
进行spi的基础配置
配置OLED的部分接口
主要就是这两个的配置 其他的什么时钟树,文件名的配置根据自己的要求来就行。
接下来就是kile5的方面了:
这是自己添加 的所需要的文件(.c和.h)
在工程中加入自己需要的文件和路径
在主函数main.c中添加头文件
接下来就是在OLED.C中写
这里代码复制的有点乱 大家在看的时候一定要注意前面的序号 避免写错。
oled.h
在主函数main.c中添加OLED相关代码就行(在这里就只是举例 ,你可以写任何你想在显示屏上显示的内容)
4.IIC驱动mpu6050
stm32cuhumx就不在多写了,直接进入kile5:
跟之前一样 最好把自己要写的部分放在文件夹中,创建.c .h 文件
IIC配置
IIC.h
#ifndef __MPUIIC_H
#define __MPUIIC_H
#include "sys.h"
//正点原子MPU9250通讯线驱动
//由正点原子MPU6050驱动修改
//IO方向设置
#define MPU_SDA_IN() {GPIOB->CRH&=0XFFFF0FFF;GPIOB->CRH|=8<<12;}
#define MPU_SDA_OUT() {GPIOB->CRH&=0XFFFF0FFF;GPIOB->CRH|=3<<12;}
//IO操作函数
#define MPU_IIC_SCL PBout(10) //SCL
#define MPU_IIC_SDA PBout(11) //SDA
#define MPU_READ_SDA PBin(11) //输入SDA
//IIC所有操作函数
void MPU_IIC_Delay(void); //MPU IIC延时函数
void MPU_IIC_Init(void); //初始化IIC的IO口
void MPU_IIC_Start(void); //发送IIC开始信号
void MPU_IIC_Stop(void); //发送IIC停止信号
void MPU_IIC_Send_Byte(u8 txd); //IIC发送一个字节
u8 MPU_IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 MPU_IIC_Wait_Ack(void); //IIC等待ACK信号
void MPU_IIC_Ack(void); //IIC发送ACK信号
void MPU_IIC_NAck(void); //IIC不发送ACK信号
void IMPU_IC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 MPU_IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif
mpu6050.c部分
#include "mpu9250.h"
#include "sys.h"
#include "delay.h"
#include "usart.h"
//MPU9250初始化程序
//由正点原子MPU6050驱动修改
//初始化MPU9250
//返回值:0,成功
// 其他,错误代码
u8 MPU_Init(void)
{
u8 res;
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);//使能AFIO时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//先使能外设IO PORTA时钟
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; // 端口配置
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //IO口速度为50MHz
GPIO_Init(GPIOA, &GPIO_InitStructure); //根据设定参数初始化GPIOA
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE);//禁止JTAG,从而PA15可以做普通IO使用,否则PA15不能做普通IO!!!
MPU_AD0_CTRL=0; //控制MPU6050的AD0脚为低电平,从机地址为:0X68
MPU_IIC_Init();//初始化IIC总线
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X80); //复位MPU6050
delay_ms(100);
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X00); //唤醒MPU6050
MPU_Set_Gyro_Fsr(3); //陀螺仪传感器,±2000dps
MPU_Set_Accel_Fsr(0); //加速度传感器,±2g
MPU_Set_Rate(50); //设置采样率50Hz
MPU_Write_Byte(MPU_INT_EN_REG,0X00); //关闭所有中断
MPU_Write_Byte(MPU_USER_CTRL_REG,0X00); //I2C主模式关闭
MPU_Write_Byte(MPU_FIFO_EN_REG,0X00); //关闭FIFO
MPU_Write_Byte(MPU_INTBP_CFG_REG,0X80); //INT引脚低电平有效
res=MPU_Read_Byte(MPU_DEVICE_ID_REG);
printf("ID:%d\r\n",res); //十进制接收值为113,十六进制接收值为71
if(res==MPU6500_ID)//器件ID正确
{
MPU_Write_Byte(MPU_PWR_MGMT1_REG,0X01); //设置CLKSEL,PLL X轴为参考
MPU_Write_Byte(MPU_PWR_MGMT2_REG,0X00); //加速度与陀螺仪都工作
MPU_Set_Rate(50); //设置采样率为50Hz
}else return 1;
return 0;
}
//设置MPU9250陀螺仪传感器满量程范围
//fsr:0,±250dps;1,±500dps;2,±1000dps;3,±2000dps
//返回值:0,设置成功
// 其他,设置失败
u8 MPU_Set_Gyro_Fsr(u8 fsr)
{
return MPU_Write_Byte(MPU_GYRO_CFG_REG,fsr<<3);//设置陀螺仪满量程范围
}
//设置MPU9250加速度传感器满量程范围
//fsr:0,±2g;1,±4g;2,±8g;3,±16g
//返回值:0,设置成功
// 其他,设置失败
u8 MPU_Set_Accel_Fsr(u8 fsr)
{
return MPU_Write_Byte(MPU_ACCEL_CFG_REG,fsr<<3);//设置加速度传感器满量程范围
}
//设置MPU9250的数字低通滤波器
//lpf:数字低通滤波频率(Hz)
//返回值:0,设置成功
// 其他,设置失败
u8 MPU_Set_LPF(u16 lpf)
{
u8 data=0;
if(lpf>=188)data=1;
else if(lpf>=98)data=2;
else if(lpf>=42)data=3;
else if(lpf>=20)data=4;
else if(lpf>=10)data=5;
else data=6;
return MPU_Write_Byte(MPU_CFG_REG,data);//设置数字低通滤波器
}
//设置MPU9250的采样率(假定Fs=1KHz)
//rate:4~1000(Hz)
//返回值:0,设置成功
// 其他,设置失败
u8 MPU_Set_Rate(u16 rate)
{
u8 data;
if(rate>1000)rate=1000;
if(rate<4)rate=4;
data=1000/rate-1;
data=MPU_Write_Byte(MPU_SAMPLE_RATE_REG,data); //设置数字低通滤波器
return MPU_Set_LPF(rate/2); //自动设置LPF为采样率的一半
}
//得到温度值
//返回值:温度值(扩大了100倍)
short MPU_Get_Temperature(void)
{
u8 buf[2];
short raw;
float temp;
MPU_Read_Len(MPU_ADDR,MPU_TEMP_OUTH_REG,2,buf);
raw=((u16)buf[0]<<8)|buf[1];
temp=36.53+((double)raw)/340;
return temp*100;;
}
//得到陀螺仪值(原始值)
//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
//返回值:0,成功
// 其他,错误代码
u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz)
{
u8 buf[6],res;
res=MPU_Read_Len(MPU_ADDR,MPU_GYRO_XOUTH_REG,6,buf);
if(res==0)
{
*gx=((u16)buf[0]<<8)|buf[1];
*gy=((u16)buf[2]<<8)|buf[3];
*gz=((u16)buf[4]<<8)|buf[5];
}
return res;;
}
//得到加速度值(原始值)
//gx,gy,gz:陀螺仪x,y,z轴的原始读数(带符号)
//返回值:0,成功
// 其他,错误代码
u8 MPU_Get_Accelerometer(short *ax,short *ay,short *az)
{
u8 buf[6],res;
res=MPU_Read_Len(MPU_ADDR,MPU_ACCEL_XOUTH_REG,6,buf);
if(res==0)
{
*ax=((u16)buf[0]<<8)|buf[1];
*ay=((u16)buf[2]<<8)|buf[3];
*az=((u16)buf[4]<<8)|buf[5];
}
return res;;
}
//IIC连续写
//addr:器件地址
//reg:寄存器地址
//len:写入长度
//buf:数据区
//返回值:0,正常
// 其他,错误代码
u8 MPU_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
u8 i;
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令
if(MPU_IIC_Wait_Ack()) //等待应答
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg); //写寄存器地址
MPU_IIC_Wait_Ack(); //等待应答
for(i=0;i<len;i++)
{
MPU_IIC_Send_Byte(buf[i]); //发送数据
if(MPU_IIC_Wait_Ack()) //等待ACK
{
MPU_IIC_Stop();
return 1;
}
}
MPU_IIC_Stop();
return 0;
}
//IIC连续读
//addr:器件地址
//reg:要读取的寄存器地址
//len:要读取的长度
//buf:读取到的数据存储区
//返回值:0,正常
// 其他,错误代码
u8 MPU_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令
if(MPU_IIC_Wait_Ack()) //等待应答
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg); //写寄存器地址
MPU_IIC_Wait_Ack(); //等待应答
MPU_IIC_Start();
MPU_IIC_Send_Byte((addr<<1)|1);//发送器件地址+读命令
MPU_IIC_Wait_Ack(); //等待应答
while(len)
{
if(len==1)*buf=MPU_IIC_Read_Byte(0);//读数据,发送nACK
else *buf=MPU_IIC_Read_Byte(1); //读数据,发送ACK
len--;
buf++;
}
MPU_IIC_Stop(); //产生一个停止条件
return 0;
}
//IIC写一个字节
//reg:寄存器地址
//data:数据
//返回值:0,正常
// 其他,错误代码
u8 MPU_Write_Byte(u8 reg,u8 data)
{
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);//发送器件地址+写命令
if(MPU_IIC_Wait_Ack()) //等待应答
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Send_Byte(reg); //写寄存器地址
MPU_IIC_Wait_Ack(); //等待应答
MPU_IIC_Send_Byte(data);//发送数据
if(MPU_IIC_Wait_Ack()) //等待ACK
{
MPU_IIC_Stop();
return 1;
}
MPU_IIC_Stop();
return 0;
}
//IIC读一个字节
//reg:寄存器地址
//返回值:读到的数据
u8 MPU_Read_Byte(u8 reg)
{
u8 res;
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR<<1)|0);//发送器件地址+写命令
MPU_IIC_Wait_Ack(); //等待应答
MPU_IIC_Send_Byte(reg); //写寄存器地址
MPU_IIC_Wait_Ack(); //等待应答
MPU_IIC_Start();
MPU_IIC_Send_Byte((MPU_ADDR<<1)|1);//发送器件地址+读命令
MPU_IIC_Wait_Ack(); //等待应答
res=MPU_IIC_Read_Byte(0);//读取数据,发送nACK
MPU_IIC_Stop(); //产生一个停止条件
return res;
}
mpu6050.h
#ifndef __MPU9250_H
#define __MPU9250_H
#include "mpuiic.h"
//MPU9250 AD0控制脚
#define MPU_AD0_CTRL PAout(15) //控制AD0电平,从而控制MPU地址
//#define MPU_ACCEL_OFFS_REG 0X06 //accel_offs寄存器,可读取版本号,寄存器手册未提到
//#define MPU_PROD_ID_REG 0X0C //prod id寄存器,在寄存器手册未提到
#define MPU_SELF_TESTX_REG 0X0D //自检寄存器X
#define MPU_SELF_TESTY_REG 0X0E //自检寄存器Y
#define MPU_SELF_TESTZ_REG 0X0F //自检寄存器Z
#define MPU_SELF_TESTA_REG 0X10 //自检寄存器A
#define MPU_SAMPLE_RATE_REG 0X19 //采样频率分频器
#define MPU_CFG_REG 0X1A //配置寄存器
#define MPU_GYRO_CFG_REG 0X1B //陀螺仪配置寄存器
#define MPU_ACCEL_CFG_REG 0X1C //加速度计配置寄存器
#define MPU_MOTION_DET_REG 0X1F //运动检测阀值设置寄存器
#define MPU_FIFO_EN_REG 0X23 //FIFO使能寄存器
#define MPU_I2CMST_CTRL_REG 0X24 //IIC主机控制寄存器
#define MPU_I2CSLV0_ADDR_REG 0X25 //IIC从机0器件地址寄存器
#define MPU_I2CSLV0_REG 0X26 //IIC从机0数据地址寄存器
#define MPU_I2CSLV0_CTRL_REG 0X27 //IIC从机0控制寄存器
#define MPU_I2CSLV1_ADDR_REG 0X28 //IIC从机1器件地址寄存器
#define MPU_I2CSLV1_REG 0X29 //IIC从机1数据地址寄存器
#define MPU_I2CSLV1_CTRL_REG 0X2A //IIC从机1控制寄存器
#define MPU_I2CSLV2_ADDR_REG 0X2B //IIC从机2器件地址寄存器
#define MPU_I2CSLV2_REG 0X2C //IIC从机2数据地址寄存器
#define MPU_I2CSLV2_CTRL_REG 0X2D //IIC从机2控制寄存器
#define MPU_I2CSLV3_ADDR_REG 0X2E //IIC从机3器件地址寄存器
#define MPU_I2CSLV3_REG 0X2F //IIC从机3数据地址寄存器
#define MPU_I2CSLV3_CTRL_REG 0X30 //IIC从机3控制寄存器
#define MPU_I2CSLV4_ADDR_REG 0X31 //IIC从机4器件地址寄存器
#define MPU_I2CSLV4_REG 0X32 //IIC从机4数据地址寄存器
#define MPU_I2CSLV4_DO_REG 0X33 //IIC从机4写数据寄存器
#define MPU_I2CSLV4_CTRL_REG 0X34 //IIC从机4控制寄存器
#define MPU_I2CSLV4_DI_REG 0X35 //IIC从机4读数据寄存器
#define MPU_I2CMST_STA_REG 0X36 //IIC主机状态寄存器
#define MPU_INTBP_CFG_REG 0X37 //中断/旁路设置寄存器
#define MPU_INT_EN_REG 0X38 //中断使能寄存器
#define MPU_INT_STA_REG 0X3A //中断状态寄存器
#define MPU_ACCEL_XOUTH_REG 0X3B //加速度值,X轴高8位寄存器
#define MPU_ACCEL_XOUTL_REG 0X3C //加速度值,X轴低8位寄存器
#define MPU_ACCEL_YOUTH_REG 0X3D //加速度值,Y轴高8位寄存器
#define MPU_ACCEL_YOUTL_REG 0X3E //加速度值,Y轴低8位寄存器
#define MPU_ACCEL_ZOUTH_REG 0X3F //加速度值,Z轴高8位寄存器
#define MPU_ACCEL_ZOUTL_REG 0X40 //加速度值,Z轴低8位寄存器
#define MPU_TEMP_OUTH_REG 0X41 //温度值高八位寄存器
#define MPU_TEMP_OUTL_REG 0X42 //温度值低8位寄存器
#define MPU_GYRO_XOUTH_REG 0X43 //陀螺仪值,X轴高8位寄存器
#define MPU_GYRO_XOUTL_REG 0X44 //陀螺仪值,X轴低8位寄存器
#define MPU_GYRO_YOUTH_REG 0X45 //陀螺仪值,Y轴高8位寄存器
#define MPU_GYRO_YOUTL_REG 0X46 //陀螺仪值,Y轴低8位寄存器
#define MPU_GYRO_ZOUTH_REG 0X47 //陀螺仪值,Z轴高8位寄存器
#define MPU_GYRO_ZOUTL_REG 0X48 //陀螺仪值,Z轴低8位寄存器
#define MPU_I2CSLV0_DO_REG 0X63 //IIC从机0数据寄存器
#define MPU_I2CSLV1_DO_REG 0X64 //IIC从机1数据寄存器
#define MPU_I2CSLV2_DO_REG 0X65 //IIC从机2数据寄存器
#define MPU_I2CSLV3_DO_REG 0X66 //IIC从机3数据寄存器
#define MPU_I2CMST_DELAY_REG 0X67 //IIC主机延时管理寄存器
#define MPU_SIGPATH_RST_REG 0X68 //信号通道复位寄存器
#define MPU_MDETECT_CTRL_REG 0X69 //运动检测控制寄存器
#define MPU_USER_CTRL_REG 0X6A //用户控制寄存器
#define MPU_PWR_MGMT1_REG 0X6B //电源管理寄存器1
#define MPU_PWR_MGMT2_REG 0X6C //电源管理寄存器2
#define MPU_FIFO_CNTH_REG 0X72 //FIFO计数寄存器高八位
#define MPU_FIFO_CNTL_REG 0X73 //FIFO计数寄存器低八位
#define MPU_FIFO_RW_REG 0X74 //FIFO读写寄存器
#define MPU_DEVICE_ID_REG 0X75 //器件ID寄存器
//如果AD0脚(9脚)接地,IIC地址为0X68(不包含最低位).
//如果接V3.3,则IIC地址为0X69(不包含最低位).
#define MPU_ADDR 0X68
#define MPU6500_ID 0X71 //MPU6500的器件ID
//AK8963的内部寄存器
#define MAG_WIA 0x00 //AK8963的器件ID寄存器地址
#define MAG_CNTL1 0X0A
#define MAG_CNTL2 0X0B
#define MAG_XOUT_L 0X03
#define MAG_XOUT_H 0X04
#define MAG_YOUT_L 0X05
#define MAG_YOUT_H 0X06
#define MAG_ZOUT_L 0X07
#define MAG_ZOUT_H 0X08
因为模块AD0默认接GND,所以转为读写地址后,为0XD1和0XD0(如果接VCC,则为0XD3和0XD2)
//#define MPU_READ 0XD1
//#define MPU_WRITE 0XD0
u8 MPU_Init(void); //初始化MPU6050
u8 MPU_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf);//IIC连续写
u8 MPU_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf); //IIC连续读
u8 MPU_Write_Byte(u8 reg,u8 data); //IIC写一个字节
u8 MPU_Read_Byte(u8 reg); //IIC读一个字节
u8 MPU_Set_Gyro_Fsr(u8 fsr);
u8 MPU_Set_Accel_Fsr(u8 fsr);
u8 MPU_Set_LPF(u16 lpf);
u8 MPU_Set_Rate(u16 rate);
u8 MPU_Set_Fifo(u8 sens);
short MPU_Get_Temperature(void);
u8 MPU_Get_Gyroscope(short *gx,short *gy,short *gz);
u8 MPU_Get_Accelerometer(short *ax,short *ay,short *az);
#endif
主函数main.c
void SYS_Init(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组函数
delay_init(); //延时函数初始化
uart_init(115200); //串口初始化为115200
LED_Init(); //初始化与LED连接的硬件接口
OLED_Init(); //OLED初始化
if(MPU_Init() == 0) //初始化MPU9250
printf("ID读取正常\r\n"); //ID读取正常
else
printf("ID读取不正常\r\n"); //ID读取不正常
while(1)
{
error = mpu_dmp_init(); //初始化mpu_dmp库
printf("ERROR:%d\r\n",error); //串口显示初始化错误值
switch (error) //对不同错误值类型判断
{
case 0:printf("DMP库初始化正常\r\n");break;
case 1:printf("设置传感器失败\r\n");break;
case 2:printf("设置FIFO失败\r\n");break;
case 3:printf("设置采样率失败\r\n");break;
case 4:printf("加载dmp固件失败\r\n");break;
case 5:printf("设置陀螺仪方向失败\r\n");break;
case 6:printf("设置dmp功能失败\r\n");break;
case 7:printf("设置DMP输出速率失败\r\n");break;
case 8:printf("自检失败\r\n");break;
case 9:printf("使能DMP失败\r\n");break;
case 10:printf("初始化MPU6050失败\r\n");break;
default :printf("未知错误\r\n");break;
}
if(error == 0)break; //如果没有错误直接退出循环
delay_ms(200); //延时
LED0 =! LED0; //LED0闪烁报错
sprintf((char *)tmp_buf," ERROR:%d",error);//字符串格式化命令
OLED_ShowString(0,0,(u8 *)tmp_buf,16); //OLED显示错误值
OLED_Refresh(); //刷新显存
}
delay_ms(999);
OLED_ShowString(0,0,(u8*)" MPU9250",16); //OLED显示字符串
OLED_Refresh(); //刷新显存
}
/**
* @brief MPU6050数据读取函数
* @param 无
* @retval 无
*/
void MPU_Read(void)
{
if(mpu_dmp_get_data(&yaw,&pitch,&roll)==0) //dmp处理得到数据,对返回值进行判断
{
printf("Pitch:%f Roll:%f Yaw:%f\r\n",pitch,roll,yaw);//串口输出三轴角度
mpu9250.speed++; //显示速度自加
if(mpu9250.speed == 1) //显示速度阈值设置
{
mpu9250.flag = 1; //采集成功标志位设置为有效
mpu9250.speed = 0; //显示速度归零
}
}
else //采集不成功
{
mpu9250.flag = 0; //采集成功标志位设置为无效
}
}
/**
* @brief MPU6050数据上报
* @param 无
* @retval 无
*/
void DATA_Report(void)
{
if(mpu9250.flag == 1) //采集成功时
{
temp=pitch*100; //赋temp为pitch
if(temp<0) //对数据正负判断,判断为负时
{
temp=-temp; //对负数据取反
sprintf((char *)tmp_buf," Pitch:-%.3d.%.2d",temp/100,temp%100);//字符串格式化命令
}
else //判断为正时
{
sprintf((char *)tmp_buf," Pitch: %.3d.%.2d",temp/100,temp%100);//字符串格式化命令
}
OLED_ShowString(0,16,(u8 *)tmp_buf,16); //OLED显示字符串
temp=roll*100; //赋temp为pitch
if(temp<0) //对数据正负判断,判断为负时
{
temp=-temp; //对负数据取反
sprintf((char *)tmp_buf," Roll :-%.3d.%.2d",temp/100,temp%100);//字符串格式化命令
}
else //判断为正时
{
sprintf((char *)tmp_buf," Roll : %.3d.%.2d",temp/100,temp%100);//字符串格式化命令
}
OLED_ShowString(0,48,(u8 *)tmp_buf,16); //OLED显示字符串
temp=yaw*100; //赋temp为pitch
if(temp<0) //对数据正负判断,判断为负时
{
temp=-temp; //对负数据取反
sprintf((char *)tmp_buf," Yaw :-%.3d.%.2d",temp/100,temp%100);//字符串格式化命令
}
else //判断为正时
{
sprintf((char *)tmp_buf," Yaw : %.3d.%.2d",temp/100,temp%100);//字符串格式化命令
}
OLED_ShowString(0,32,(u8 *)tmp_buf,16); //OLED显示字符串
OLED_Refresh(); //刷新显存
LED1=!LED1; //LED闪烁
mpu9250.flag = 0; //采集成功标志位设置为无效
}
else ; //防卡死
}
主要的几个模块的代码就在这里 ,其余细节的没有写。