STM32的SPI及IIC

目录

STM32的SPI及IIC

1.SPI的配置方式

SPI**简介**

SPI**特征**

SPI**功能描述**

SPI通信配置

2.IIC 的配置方式

IIC简介

I2C 初始化结构体详解

I2C的配置 (stm32cubemx)

3.SPI 驱动OLED屏幕

4.IIC驱动mpu6050


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 ;                                          //防卡死
 }

主要的几个模块的代码就在这里 ,其余细节的没有写。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值