[DAY 11] _软件实现I2C协议以三轴感测器为例 (ADXL345)

昨天DAY10讲了控制GPIO口来完成协议,今天来讲实际的例子,以大家最常听过三轴感测器为例,首先介绍一下这个感测器,ADXL345 是一款低功耗三轴加速度计,非常适合移动设备应用,可以在倾斜检测应用中测量静态重力加速度,还可以测量动态加速度,也可检测自由落体。

昨天说了这两个函式(bdp_I2C_GPIO.c和bdp_I2C_GPIO.h),今天就来讲解如何利用昨天写的函式来读取三轴感测器,
先讲解我的bdp_I2C_ADXL345.h
首先,我先附上Datasheet网址:
https://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf (英文版本)
https://www.analog.com/media/cn/technical-documentation/data-sheets/ADXL345_cn.pdf (简体翻译版本)
接下我会搭配这个手册做部分讲解,当然这手册有中文版本了是大陆翻译的,我不建议只看中文的,有可能会有错误,真的看不懂可以交叉着看,毕竟这是一间大公司ANALOG DEVICES写出来的手册,我是相信不会有问题,有问题就不会出产这颗感测器了。

bdp_I2C_ADXL345.h

#ifndef __BSP_I2C_adxl_H
#define __BSP_I2C_adxl_H

#include "bsp_I2C_gpio.h"

#define DEVICE_ID       0X00    //器件ID,0XE5
#define THRESH_TAP      0X1D    //敲击阀值
#define OFSX            0X1E
#define OFSY            0X1F
#define OFSZ            0X20
#define DUR             0X21
#define Latent          0X22
#define Window          0X23 
#define THRESH_ACK      0X24
#define THRESH_INACT    0X25 
#define TIME_INACT      0X26
#define ACT_INACT_CTL   0X27     
#define THRESH_FF       0X28    
#define TIME_FF         0X29 
#define TAP_AXES        0X2A  
#define ACT_TAP_STATUS  0X2B 
#define BW_RATE         0X2C 
#define POWER_CTL       0X2D 
#define INT_ENABLE      0X2E
#define INT_MAP         0X2F
#define INT_SOURCE      0X30
#define DATA_FORMAT     0X31
#define DATA_X0         0X32
#define DATA_X1         0X33
#define DATA_Y0         0X34
#define DATA_Y1         0X35
#define DATA_Z0         0X36
#define DATA_Z1         0X37
#define FIFO_CTL        0X38
#define FIFO_STATUS     0X39

//如果ALT ADDRESS脚(12脚)接地,ADXL设备地址为0X53(不包含最低位).
//如果接V3.3,则ADXL设备地址为0X1D(不包含最低位).
//如果Pin12脚接3.3V,为0X3B和0X3A,如果接GND,则为0XA7和0XA6
#define ADXL_READ    0XA7			//二进制为 1010 0111
#define ADXL_WRITE   0XA6			//二进制为 1010 0110

uint8_t ADXL345_Init(void);                         //初始化ADXL345
void ADXL345_WR_Reg(uint8_t addr,uint8_t val);      //Write_ADXL345寄存器
uint8_t ADXL345_RD_Reg(uint8_t addr);               //Read_ADXL345寄存器

首先会define这麽多东西是因为,我把手册上所有可操作的暂存器位址都记下来了,当然我这边没有全部使用到,可以看以下手册第23页的部分:
https://ithelp.ithome.com.tw/upload/images/20210924/20141979YJcVWJEOpN.png
可以看到上图我红框的部分,这是三个轴读取的暂存器位置部分,这个每个脚位都有对应详细功能说明,在手册接下去24页後的部分,最後几行是函式的宣告,再来讲解bdp_I2C_ADXL345.c

bdp_I2C_ADXL345.c

uint8_t ADXL345_Init(void)
{ 
//初始化IIC
 GPIO_InitTypeDef GPIO_InitStructure;
 RCC_AHBPeriphClockCmd(I2C_GPIO_CLK, ENABLE);	/* 打开GPIO时钟 */
 GPIO_InitStructure.GPIO_Pin = I2C_SCL_PIN | I2C_SDA_PIN;
 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;  	
 GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;  	/* 开漏输出 */
 GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
 GPIO_Init(I2C_GPIO_PORT, &GPIO_InitStructure);
 /* 给一个停止信号, 重定I2C汇流排上的所有设备到待机模式 */
 i2c_Stop();     
	//------------------------------------------------
	if(ADXL345_RD_Reg(DEVICE_ID)==0XE5) //ID
	{
	ADXL345_WR_Reg(DATA_FORMAT,0X2B);  //**0x31** 低电平中断输出,13位元全解析度,输出资料右对齐,16g量程
	ADXL345_WR_Reg(BW_RATE,0x0A);       //**0x2C** 资料输出速度为400Hz				
	ADXL345_WR_Reg(POWER_CTL,0x28);     //**0X2D** 连结使能,测量模式
	ADXL345_WR_Reg(INT_ENABLE,0x00);    //不使用中断 
	//*******偏移寄存器**************		
	ADXL345_WR_Reg(OFSX,0x00);			//00  X=FB,Y=0xF9 ___ //98 X=00,Y=FE,Z=EE
	ADXL345_WR_Reg(OFSY,0x00);			//算法   1LSB=15.6mg
	ADXL345_WR_Reg(OFSZ,0x00);  		//先算偏移多少个LSB在,用2's表示负号
	return 0;
	}
  return 1;                                     
}

这是ADXL345的初始化函式,先对GPIO口的初始化,再来设定感测的模式ADXL345_WR_Reg为对暂存器写数值,至於要写数值手册24页後有说,这边先不详谈,等说完这整个配置我再详细讲解,ADXL345_RD_Reg是对暂存器读取数值
接下面是写暂存和读取暂存器出来的数值函式

//写ADXL345寄存器	  addr:暂存器地址		al:要写入的值
void ADXL345_WR_Reg(uint8_t addr,uint8_t val)  //void ADXL345_write(u8 addr, u8 data)
{
	i2c_Start();                 
	i2c_SendByte(ADXL_WRITE);   
	i2c_WaitAck();    
	i2c_SendByte(addr);
	i2c_WaitAck();                                                         
	i2c_SendByte(val);                                
	i2c_WaitAck();                    
	i2c_Stop();                     
	
} 
//读ADXL345寄存器		addr:寄存器地址		返回值:读到的值
uint8_t ADXL345_RD_Reg(uint8_t addr)
{
	uint8_t temp=0;  
	i2c_Start();                   
	i2c_SendByte(ADXL_WRITE);      
	i2c_WaitAck();       
	i2c_SendByte(addr);        //发送寄存器地址
	i2c_WaitAck();                                                       
	i2c_Start();               
	i2c_SendByte(ADXL_READ);       
	i2c_WaitAck();        
	temp=i2c_ReadByte();       
	i2c_NAck();
	i2c_Stop();                      
	return temp;              
} 

至於这时需如何观看手册写入时序,我明天会再一步步讲解搭配手册讲解

然後main.c可以来使用这些函式读值啦~,附上我的主程序

#include "bsp_I2C_adxl345.h"
#include "bsp_SysTick.h"    //操作MO内核暂存器使用滴答计时器来做精准计时
                            //参考网站:https://kknews.cc/zh-tw/news/96p52m5.html
int main(void)
{
  int8_t x0,x1,y0,y1,z1,z0; //宣告3个轴的变数,1个轴有高8为和低8位
  SysTick_Init(48);         //配置Delay函式
  
  DEBUG_USART_Config();     //初始化UART
  ADXL345_Init();           //初始化ADXL345
  
  while(1)
  {
    x0=ADXL345_RD_Reg(0xAA);// 取得 X 轴 低位元资料
	x1=ADXL345_RD_Reg(0x33);// 取得 X 轴 高位元资料
	x=(((x1 << 8)+x0)/256.0);
    
	y0=ADXL345_RD_Reg(0x34);// 取得 Y 轴 低位元资料
    y1=ADXL345_RD_Reg(0x35);// 取得 Y 轴 高位元资料
    y=(((y1 << 8)+y0)/256.0);
    
    z0=ADXL345_RD_Reg(0x36);// 取得 Z 轴 低位元资料
    z1=ADXL345_RD_Reg(0x37);// 取得 Y 轴 高位元资料
    z=(((z1 << 8)+z0)/256.0);
    
	printf("X=%.3f  Y=%.3f  Z=%.3f\r\n",x,y,z);
	delay_ms(100);
   }
}

以上看到的写法,明天会再搭配手册慢慢讲解,想学嵌入式学看datasheet(数据手册)是必经之路~~
可以先自己看看,我想信很多人都看的我这再写什麽...,最後一行的printf是利用写好的函是让UART传出数值,这样我就可以用串口视窗来查看三个轴的数值啦~~~


<<:  Day11.进入 ARM 世界: ARM Cortex-M Exception Registers

>>:  Day 24 - Tailwind Plugin 使用 (三) => Forms

Day3-TypeScript(TS)安装开发环境

经过两天的简介,希望大家都对TypeScript(TS)有基本的了解。 今天呢要来讲解安装TS的开发...

【心得】Sublime TexT 3 即时连线

一开始学习时用Sublime,久了之後也成为一种习惯(,,・ω・,,) 但是初学者很喜欢写一步骤就要...

Day14-Vue CLI 介绍

他是Vue.js官方提供的开发工具,可以快速的建置架构,常用於制作单页应用(SPA)网站。 安装 安...

[Day11] 以神经网络进行时间序列预测 — RNN

本篇详细介绍 RNN 并使用它进行时间序列预测 本日大纲 RNN 介绍 激活函数 RNN 的分类 时...

[Day07] TS:什麽是 Utility Types?

上面这个是今天会提到的内容,如果你已经可以轻松看懂,欢迎直接左转去看我同事 Andy 「前端工程师...