最近在项目中要用到飞思卡尔的MMA7660这一款传感器,我在STM32中使用的是模拟I2C来读写它的寄存器,闲话不多说,代码如下:
1、IO引脚配置和模拟I2C的方法
/******************** (C) COPYRIGHT 2015 ASTO **************************
@* 文件名 :mma7660.c
@* 描述 :飞思卡尔的MMA7660系列重力加速度传感器
@* 开发平台:STM32F103CBT6系列处理器
@* 通信方式:I2C
@* 硬件连接: ------------------------------
* | PB1 : 中断引脚 |
* | PB10 : SCL数据线 |
* | PB11 : SDA数据线 |
* ------------------------------
@* 库版本 :ST3.5.0
@* 开发者 :POWER
************************************************************************/
#include "stm32f10x.h"
#include "mma7660.h"
#include "delay.h"
/*
*=========================================================
* 函数功能:MMA7660中断向量表配置
* 参数: 无
*
* 函数返回值:无
*=========================================================
*/
static void MMA7660_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* Enable the EXTI1 Interrupt */
NVIC_InitStructure.NVIC_IRQChannel = MMA7660_INT_IRQ; //中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//次优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //通道中断使能
NVIC_Init(&NVIC_InitStructure);//初始化中断
}
/*
*=========================================================
* 函数功能:MMA7660中断线配置
* 参数: 无
*
* 函数返回值:无
*=========================================================
*/
static void MMA7660_ExtiInit(void)
{
EXTI_InitTypeDef EXTI_InitStructure;
MMA7660_NVIC_Config();
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
//配置MMA7660模块发生相应的事件后中断配置
GPIO_EXTILineConfig(MMA7660_INT_PORT_SRC, MMA7660_INT_SOURCE);
EXTI_InitStructure.EXTI_Line = MMA7660_INT_LINE; //中断线
EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//使用中断方式触发模式(另外还有事件方式触发模式)
EXTI_InitStructure.EXTI_Trigger = MMA7660_INT_MODE; //中断信号触发边沿
EXTI_InitStructure.EXTI_LineCmd = ENABLE; //中断线使能
EXTI_Init(&EXTI_InitStructure); //初始化中断
}
/*
**********************************************************
*
* IIC总线模拟程序
*
**********************************************************
*/
/*
*=========================================================
* 函数功能:MMA7660端口状态配置
* 参数: 无
*
* 函数返回值:无
*=========================================================
*/
void MMA7660_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
//中断引脚配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure); // 选择B端口
//SCL数据线配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 普通推挽输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 最高输出速率50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); // 选择B端口
//SDA数据线配置
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 最高输出速率50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); // 选择B端口
MMA7660_ExtiInit(); //中断配置
MMA7660_Begin(); //MMA7660寄存器配置
}
/*
*=========================================================
* 函数功能:MMA7660的SDA数据线配置,这里作为输出用
* 参数: 无
*
* 函数返回值:无
*=========================================================
*/
void MMA_SDA_IOOUT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; // 开漏输出
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 最高输出速率50MHz
GPIO_Init(GPIOB, &GPIO_InitStructure); // 选择B端口
}
/*
*=========================================================
* 函数功能:MMA7660的SDA数据线配置,这里作为输入用
* 参数: 无
*
* 函数返回值:无
*=========================================================
*/
void MMA_SDA_IOIN(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_11 ;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
GPIO_Init(GPIOB, &GPIO_InitStructure); // 选择B端口
}
/*
*=========================================================
* 函数功能:IIC总线初始化信号
* 参数:无
* 函数返回值:无
* SDA ---
* |________
* SCL ----------
* |____
*=========================================================
*/
void I2C_Start(void)
{
MMA7660_SCL_H;
MMA7660_SDA_H;
Delayus(1);
MMA7660_SDA_L;
Delayus(1);
}
/*
*=========================================================
* 函数功能:IIC总线停止信号
* 参数:无
* 函数返回值:无
* SDA ---------
* ______|
* SCL ---------
* |___
*=========================================================
*/
void I2C_Stop(void)
{
MMA7660_SCL_H;
MMA7660_SDA_L;
Delayus(1);
MMA7660_SDA_H;
Delayus(1);
}
/*
*=========================================================
* 函数功能:接收由从机向主机发起的ACK信号。
* 参数: ACKBIT
*
* 函数返回值:MMA_ERROR/MMA_OK
*=========================================================
*/
uint8_t I2C_SlaveAck(void)
{
uint8_t ts=0;
MMA7660_SCL_L;
MMA7660_SDA_H;
MMA_SDA_IOIN(); //SDA设为输入
Delayus(1);
MMA7660_SCL_H;
while(MMA7660_SDA_D!=0) {
ts++;
if(ts>200) {
MMA_SDA_IOOUT(); //SDA设为输出
MMA7660_SCL_L;
return MMA_ERROR; //返回错误
}
}
MMA7660_SCL_L;
MMA_SDA_IOOUT(); //SDA设为输出
//MMA7660_SDA_H;
Delayus(1);
return MMA_OK; //返回错误
}
/*
*=========================================================
* 函数功能:IIC写一个字节数据
* 参数: a:需要写入的一个字节数据
* 函数返回值:无
*=========================================================
*/
void I2C_WriteByte(uint8_t a)
{
uint8_t i;
for(i=0; i<8; i++)
{
MMA7660_SCL_L;
if((a&0x80)!=0) MMA7660_SDA_H;
else MMA7660_SDA_L;
a <<= 1;
Delayus(1);
MMA7660_SCL_H;
Delayus(1);
}
MMA7660_SCL_L;
if(I2C_SlaveAck()==MMA_ERROR) { //等待从机的ACK信号
return ;
}
}
/*
*=========================================================
* 函数功能:IIC读一个字节数据
* 参数:无
* 函数返回值:返回读出的一个字节数据
*=========================================================
*/
uint8_t I2C_ReadByte(void)
{
uint8_t a =0;
uint8_t i;
MMA_SDA_IOIN(); //SDA设为输入
for(i=0; i<8; i++) {
a <<= 1;
MMA7660_SCL_H;
Delayus(1);
if(MMA7660_SDA_D==1) a |= 0x01;
Delayus(1);
MMA7660_SCL_L;
Delayus(2);
}
return a;
}
2、MMA相关的代码
/*
**********************************************************
*
* MMA7660相关函数
*
**********************************************************
*/
/*
*=========================================================
* 函数功能:写MAA7660寄存器
* 参数:
* Regs_Addr - 寄存器地址
* Regs_Data - 寄存器值
* 函数返回值:
*=========================================================
*/
void MMA7660_WriteReg(uint8_t Regs_Addr,uint8_t Regs_Data)
{
I2C_Start();
I2C_WriteByte(MMA7660_DEV_WRITE); //先写Slave地址,并配置成写模式
I2C_WriteByte(Regs_Addr); //写寄存器地址
I2C_WriteByte(Regs_Data); //写寄存器内容
I2C_Stop(); //结束本段IIC进程
}
/*
*=========================================================
* 函数功能:读MAA7660单字节
* 参数
* Regs_Addr - 寄存器地址
* 函数返回值:寄存器值
*=========================================================
*/
uint8_t MMA7660_ReadReg(uint8_t Regs_Addr)
{
uint8_t ret;
I2C_Start();
I2C_WriteByte(MMA7660_DEV_WRITE); //先写Slave地址,并配置成写模式
I2C_WriteByte(Regs_Addr); //写寄存器地址
I2C_Start();
I2C_WriteByte(MMA7660_DEV_READ); //写Slave地址,并配置成读模式
ret=I2C_ReadByte(); //从传感器中读出数据
I2C_SlaveAck();
I2C_Stop(); //结束本段IIC进程
return ret;
}
/*
*=========================================================
* 函数功能:读MAA7660加速度输出
* 参数 :
* Regs_Addr - 加速度寄存器地址
* 函数返回值:加速度值
*=========================================================
*/
uint8_t MMA7660_GetResult(uint8_t Regs_Addr)
{
uint8_t ret;
// 等待转换完成并取出值
ret=MMA7660_ReadReg(Regs_Addr);
while(ret&0x40) {
ret=MMA7660_ReadReg(Regs_Addr); //数据更新,重新读
}
return ret;
}
/*
*=========================================================
* 函数功能:MAA7660寄存器配置,决定MMA7660工作方式
* 参数 :
*
* 函数返回值:无
*=========================================================
*/
void MMA7660_Begin(void)
{
MMA7660_WriteReg(MMA7660_MODE,0x00); //standby mode //首先必须进入standby模式才能更改寄存器
MMA7660_WriteReg(MMA7660_SPCNT,0x00); //No Sleep Count
MMA7660_WriteReg(MMA7660_INTSU,0xE3);//0xE0 will be OK //original value: 0x03 //Configure GINT Interrupt
MMA7660_WriteReg(MMA7660_PDET,0xE0); //orginal value: 0xE0 //No tap detection enabled
MMA7660_WriteReg(MMA7660_SR,0x02); //orginal value: 0x34 //8 samples/s,TILT debounce filter=2
MMA7660_WriteReg(MMA7660_PD,0x00); //original value: 0x00 //No tap detection debounce count enabled
MMA7660_WriteReg(MMA7660_MODE,0x41);//original value: 0x41 //Active Mode,INT= push-pull and active low
}
3、头文件定义
#ifndef _MMA7660_H_
#define _MMA7660_H_
//硬件定义
#define MM_INT GPIO_Pin_1 // PB1
#define MM_SCL GPIO_Pin_10 // PB10
#define MM_SDA GPIO_Pin_11 // PB11
/*
**********************************************************
*
* 中断线配置,低电平触发
*
**********************************************************
*/
#define MMA7660_INT_GPIO_SRC GPIOB //外设组
#define MMA7660_INT_PORT_SRC GPIO_PortSourceGPIOB //中断引脚组
#define MMA7660_INT_IRQ EXTI1_IRQn //中断号
#define MMA7660_INT_SOURCE GPIO_PinSource1 //中断源
#define MMA7660_INT_LINE EXTI_Line1 //中断线
#define MMA7660_INT_MODE EXTI_Trigger_Falling //中断触发方式
/*
**********************************************************
*
* 相关宏定义
*
**********************************************************
*/
//==========模拟IIC==================//
#define MMA_ERROR 0
#define MMA_OK 1
#define I2C_NOACK 0
#define I2C_ACK 1
#define MMA7660_SDA GPIO_Pin_10 // PB10 //IIC数据线接口
#define MMA7660_SCL GPIO_Pin_11 // PB11 //IIC时钟线接口
#define MMA7660_INT GPIO_Pin_1 // PB1 //中断线接口
#define MMA7660_INT_D GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) //PB1读数据
#define MMA7660_SDA_D GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) //PB11读数据
#define MMA7660_SDA_H GPIO_SetBits(GPIOB, GPIO_Pin_11) // PB11 高电平
#define MMA7660_SDA_L GPIO_ResetBits(GPIOB, GPIO_Pin_11) // PB11 低电平
#define MMA7660_SCL_H GPIO_SetBits(GPIOB, GPIO_Pin_10) // PB10 高电平
#define MMA7660_SCL_L GPIO_ResetBits(GPIOB, GPIO_Pin_10) // PB10 低电平
//==========MMA7660 寄存器地址==================//
#define MMA7660_XOUT 0x00 // 6-bit output value X
#define MMA7660_YOUT 0x01 // 6-bit output value Y
#define MMA7660_ZOUT 0x02 // 6-bit output value Z
#define MMA7660_TILT 0x03 // Tilt Status
#define MMA7660_SRST 0x04 // Sampling Rate Status
#define MMA7660_SPCNT 0x05 // Sleep Count
#define MMA7660_INTSU 0x06 // Interrupt Setup
#define MMA7660_MODE 0x07 // Mode
#define MMA7660_SR 0x08 // Auto-Wake/Sleep and
// Portrait/Landscape samples
// per seconds and Debounce Filter
#define MMA7660_PDET 0x09 // Tap Detection
#define MMA7660_PD 0x0A // Tap Debounce Count
//=========MMA7660 功能参数==================//
#define MMA7660_DEV_ADDR 0x4C //Normally,can range 0x08 to 0xEF
#define MMA7660_DEV_WRITE MMA7660_DEV_ADDR<<1 | 0x00
#define MMA7660_DEV_READ MMA7660_DEV_ADDR<<1 | 0x01
/*
**********************************************************
*
* 全局函数声明
*
**********************************************************
*/
void MMA7660_Init(void); //SCL,INT端口输出配置
void MMA_SDA_IOOUT(void); //SDA端口配置为输出
void MMA_SDA_IOIN(void); //SDA端口配置为输入
void I2C_Start(void); //开始信号
void I2C_Stop(void); //结束信号
uint8_t I2C_SlaveAck(void); //应答信号
void I2C_WriteByte(uint8_t a); //写一字节
uint8_t I2C_ReadByte(void); //读一字节
void MMA7660_WriteReg(uint8_t Regs_Addr,uint8_t Regs_Data); //写寄存器
uint8_t MMA7660_ReadReg(uint8_t Regs_Addr); //读寄存器
uint8_t MMA7660_GetResult(uint8_t Regs_Addr); //读加速度
void MMA7660_Begin(void); //开始传送前的寄存器值设置
/*
**********************************************************
*
* 全局定义
*
**********************************************************
*/
#endif /* _MMA7660_H_ */
4、中断处理函数
#if MMA7660_EN > 0u
#include "mma7660.h"
/*******************************************************************************
* Function Name : EXTI1_IRQHandler
* Description : This function handles External lines 1 interrupt request.
* Input : None
* Output : None
* Return : None
*******************************************************************************/
void EXTI1_IRQHandler(void)
{
OSIntEnter(); //用于统计中断的嵌套层数,对嵌套层数+1,请注意:这适用于有内核参与的中断
if(EXTI_GetITStatus(MMA7660_INT_LINE) != RESET) //如果检测到状态位置位
{
EXTI_ClearITPendingBit(MMA7660_INT_LINE); //清除中断标志位
//发送信号量给相应的MMA7660任务控制块
if (!GPIO_ReadInputDataBit(MMA7660_INT_GPIO_SRC, GPIO_Pin_1)) { //如果读取到的是低电平,则表明产生了重力传感器事件
OS_ERR err;
MMA7660_ReadReg(MMA7660_TILT);//读取TILT寄存器,清除中断,以便下次再进入中断
//使用发送信号量的方式
//第二个参数:1. OS_OPT_POST_NONE表明在发布任务信号量之后调用任务调度程序
// 2. OS_OPT_POST_NO_SCHED则OSTaskSemPost后不会调用调度程序
OSTaskSemPost(&MMA7660_TCB,
OS_OPT_POST_NONE,
&err);
}
}
OSIntExit();//对嵌套层数减1,在退出中断前启动任务调度(适用于有u/COS-III内核参与的中断)
}
#endif
5、主函数调用
在main函数中调用如下代码即可:
#if MMA7660_EN > 0u /* MMA7660重力传感器初始化 */
MMA7660_Init();
#endif