云计算百科
云计算领域专业知识百科平台

ARM06-ADC(模数转换器)和 SPI(串行外设接口)

一、ADC(模数转换器)

1.1 ADC功能介绍

① ADC(Analog-to-Digital Converter),模拟到数字转换器,用于将连续变化的模拟信号转换为离散的数字信号,以便数字系统(如微处理器、微控制器等)能够对其进行处理和分析。

② 模拟信号一般是指连续变化的电压信号,其数值在一定范围内变化。而数字信号是由一系列离散的数字表示,只能取有限的值,通常以二进制形式表示。
③ ADC的工作原理是将模拟信号分割成一系列离散的取样,并将每个取样值转换为相应的数字表示。这个过程涉及到两个主要步骤:采样和量化。

  • 采样: ADC将连续变化的模拟信号在一定时间间隔内进行取样。取样频率决定了每秒采集的样本数,通常以赫兹(Hz)表示。采样过程通过保持并测量模拟信号在每个采样时间点的电压值来实现。
  •  量化:采样得到的连续模拟信号经过量化转换为数字形式。量化是将每个采样值映射到一个离散的数字值的过程。这通常通过比较采样值与参考电压之间的差异,并将其转换为数字表示。

④ 逐次逼近法

       ADC会将实际电压与1/2*Vref(基准电压)进行比较,如果实际电压大则结果为1,否则为0。当结果为1时,将实际电压与1/2*Vref+1/4*Vref进行比较,如果实际电压大,则结果为1,否则为0;当结果为0时,将实际电压与1/2*Vref-1/4*Vref进行比较,如果实际电压大,则结果为1,否则为0。依次类推进行比较,比较的次数越多。结果越接近于实际值。比较的次数决定了精度,这个精度被称之为ADC的分辨率。

        常见的分辨率包括8位(0 ~ 255 ), 10位(0 ~ 1023), 12位(0 ~ 4095)和16位(0 ~ 65535)。  
 如:12位ADC例子:参考电压3.3V,读取到的原始ADC值是2048,代入计算得到待测电压2048 / 4095 * 3.3≈1.65V。

⑤ IMX6ULL的ADC模块核心特性:

  • I.MX6ULL内部具有两个ADC控制器,每个ADC内部都有10个通道。都是采用逐次逼近法设计的。
  • 分辨率:每路ADC的分辨率可选,分别为8/10/12位。
  • I.MX6ULL内部ADC具有自动校准功能,因此能够保证更高的测量精度;
  • 采样率:最高采样率 1MS/s,即 1s 采用 1 百万次;最大转换速率为1MHz,即完成一次转换所需的时间最快只需要1us。
  • ADC量程:0 ~ 3.3V,基准电压设置为3.3V;
  • 时钟选择:选择ADACK,ADACK是I.MX6ULL内部提供的时钟源,只能提供给ADC外设使用,这样做的目的是保证系统处于低功耗状态时, ADC依旧能够运行。 ADACK的时钟默认为20MHz。

⑥  IMX6ULL 的 ADC 结构图

⑦ 核心寄存器(开发关键)

寄存器全称作用说明
ADCx_HC0 Control register 控制寄存器 AIEN(b7) 转换完成中断使能(默认禁用)
ADCH(b0-b4) 选择采集通道(   00000-01111 External channels 0 to 15),每次切换通道时会引发一次ADC转换
ADCx_HS Status register 状态寄存器 COCO0(b0) 转换完成标志(1=完成,0=未完成)启动一次ADC转换后等待该位置位。
ADCx_R0 Data result register 数据结果寄存器 CDATA(b0-b11) 存储转换结果(ADC 的分辨率是12,低12位有效,范围0~4095)
ADCx_CFG Configuration register 配置寄存器 OVWREN(b16) 数据复写使能位(1=使能复写,0=失能复写)
AVGS(b14-b15) 硬件平均次数,默认00(4次样本求平均)此位需要ADC_GC寄存器的AVGE位置1才能使能硬件平均
ADTRG(b13) 转换触发选择( 0 = 选择软件触发 ,1 = 不选择软件触发)
REFSEL(b11-b12)

参考电压选择( 为 00 时选择 VREFH/VREFL 这两个引脚上的电压为 参考电压固定为3.3v

ADHSC(b10)

高速转换使能位( 0  = 正常模式, 1  = 高速模式)

ADSTS(b8-b9) 设置 ADC 的采样周期,与 ADLSMP 位一起决定采样周期,默认00
ADLPC(b7)

低功耗模式配置位( 0 = 不处于低功耗模式, 1 = 处于低功耗模式)

ADIV(b5-b6)

时钟分频选择(  00 时不分频)

ADLSMP(b4) 长采样周期使能位( 0 =  短采样周期模式,1 = 长采样周期模式。搭配 ADSTS 位一起控制 ADC 的采样周期)
MODE(b2-b3) 分辨率选择位(10为 12位精度 )
ADICLK(b0-b1) 输入时钟选择位(11 为ADACK)
ADCx_GC General control register 通用控制寄存器 CAL(b7) 启动自动校准(写1启动,读0完成)
ADCO(b6)

连续转换模式使能 ( 0 = 单次,每次从R0取走数据, 1 = 多次)

AVGE(b5)

硬件平均使能位 ( 0 =硬件平均失能, 1 =硬件平均使能)

ACFE(b4) 比较功能使能位( 0 =比较功能失能, 1 =比较功能使能)
ACFGT(b3) 配置比较方法(0 = 小于,1 = 大于或等于)
ACRENb2) 范围比较功能使能位( 0 =范围比较失能, 1 =范围比较使能)
DMAAEN DMA 功能使能位( 0 =DMA 功能失能, 1 =DMA 功能使能)
ADACKEN(b0) ADACK时钟使能位(1=使能时钟,0=失能时钟)
ADCx_GS General status register 通用状态寄存器 CALF(b1) 校准失败标志(0=成功,1=失败,需写1清零)

1.2 ADC代码实现

① adc.c

//adc.c
#include "adc.h"
#include "MCIMX6Y2.h"
#include "core_ca7.h"
#include "fsl_iomuxc.h"

//ADC自校准
int adc1_calibration(void)
{
ADC1->GC |= (1 << 7); //校准使能,校准完成后此位会自动清零
while((ADC1->GC & (1 << 7)) != 0); //等待校准完成
//校准标志位:0:代表成功 1:代表失败,写1清零
if((ADC1->GS & (1 << 1)) == 0)
{
return 1;
}
else
{
return 0;
}
}

//ADC初始化
void adc1_channel_init(void)
{
//1.引脚复用
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO01_GPIO1_IO01,0);
//2.电气属性
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO01_GPIO1_IO01, IOMUXC_SW_PAD_CTL_PAD_PKE(1));
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO01_GPIO1_IO01, IOMUXC_SW_PAD_CTL_PAD_PUE(0));
//3.配置ADC
ADC1->HC[0] &= ~( 1 << 7); //禁用转换完成中断
ADC1->CFG = 0;
ADC1->CFG |= (2 << 2) | (3 << 0); //分辨率选择 12 位,时钟源选择 ADACK
ADC1->GC = 0;
ADC1->GC |= (1 << 0); //使能 ADACK 这个时钟
//ADC校准
printf(adc1_calibration() ? "calibration completed normally!\\n" : "calibration completed failed!\\n");
}
//读取ADC原始值
unsigned short adc1_get_origin_value(void)
{
//1.启动ADC转换
ADC1->HC[0] = 0; //每切换一次通道就会完成一次ADC转换
ADC1->HC[0] = 1;
//2.等待ADC装换完成
while((ADC1->HS & (1 << 0)) == 0);
//3.读取ADC原始值
return ADC1->R[0] & 0xFFF;
}
float get_adc_value()
{
//量化结果转换公式
return ((get_adc_original_value() / 4096.0) * 3.3);
}

  通过结果看到当前存在较大波动,需要进行滤波处理。常用的滤波方法包括:均值滤波、低通滤波、高通滤波以及具有预测性的卡尔曼滤波等。这里我们采用均值滤波算法进行处理,具体实现代码如下:

        其核心思路是:每次转换采样100次数据,对这些采样值进行排序后,剔除10个最大值和10个最小值,最后取剩余数据的平均值作为最终量化结果并进行转换。

//adc.c
#include "adc.h"
#include "MCIMX6Y2.h"
#include "core_ca7.h"
#include "fsl_iomuxc.h"

//ADC自校准
int adc1_calibration(void)
{
ADC1->GC |= (1 << 7);
while((ADC1->GC & (1 << 7)) != 0);
if((ADC1->GS & (1 << 1)) == 0)
{
return 1;
}
else
{
return 0;
}
}

//ADC初始化
void adc1_channel_init(void)
{
//1.引脚复用
IOMUXC_SetPinMux(IOMUXC_GPIO1_IO01_GPIO1_IO01,0);
//2.电气属性
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO01_GPIO1_IO01, IOMUXC_SW_PAD_CTL_PAD_PKE(1));
IOMUXC_SetPinConfig(IOMUXC_GPIO1_IO01_GPIO1_IO01, IOMUXC_SW_PAD_CTL_PAD_PUE(0));
//3.配置ADC
ADC1->HC[0] &= ~( 1 << 7);
ADC1->CFG = 0;
ADC1->CFG |= (2 << 2) | (3 << 0);
ADC1->GC = 0;
ADC1->GC |= (1 << 0);
//ADC校准
printf(adc1_calibration() ? "calibration completed normally!\\n" : "calibration completed failed!\\n");
}
//读取ADC原始值
unsigned short adc1_get_origin_value(void)
{
//1.启动ADC转换
ADC1->HC[0] = 0;
ADC1->HC[0] = 1;
//2.等待ADC装换完成
while((ADC1->HS & (1 << 0)) == 0);
//3.读取ADC原始值
return ADC1->R[0] & 0xFFF;

}

//交换
void swap(unsigned short *a, unsigned short *b)
{
unsigned short t = *a;
*a = *b;
*b = t;
}
//冒泡排序
void sort(unsigned short *a, int len)
{
int i = 0;
int j = 0;
for(i = 0; i < len-1; i++)
{
for(j = 0; j < len – 1 -i; j++)
{
if(a[j] > a[j + 1])
{
swap(&a[j], &a[j + 1]);
}
}
}
}

//ADC采样值求和
unsigned int sum_of_array(unsigned short *a, int len)
{
int i =0;
unsigned int sum = 0;
for(i = 0; i < len; i++)
{
sum += a[i];
}
return sum;
}

//读取ADC电压值
float adc1_get_value(void)
{
unsigned short a[100];
int i = 0;
for(i = 0; i < 100; i++)
{
a[i] = adc1_get_origin_value();
}
sort(a, sizeof(a) / sizeof(a[0]));
return sum_of_array(a + 10, 80) / 80 / 4095.0 * 3.3;
}

② main.c 

#include "MCIMX6Y2.h"
#include "fsl_iomuxc.h"
#include "led.h"
#include "beep.h"
#include "key.h"
#include "core_ca7.h"
#include "interrupt.h"
#include "clock.h"
#include "epit.h"
#include "gpt.h"
#include "delay.h"
#include "uart.h"
#include "stdio.h"
#include "i2c.h"
#include "lm75.h"
#include "adc.h"
int main(void)
{
//时钟初始化
clock_init();
//GIC初始化
system_interrupt_init();
//led初始化
led_init();
//蜂鸣器beep初始化
beep_init();
//按键初始化
key_init();
//EPIT初始化
// epit_init();
//GPT初始化
gpt_init();
//串口uart初始化
uart_init();
//I2C初始化
i2c_init();
//ADC初始化
adc1_channel_init();
led_off();
beep_off();

while (1)
{
short t = adc1_get_origin_value();
printf("origin_value:%d\\n", t);
float f = adc1_get_value();
int k = f * 1000;
int m = k / 1000;
int n = k % 1000;
printf("voltage:%d.%d\\n", m, n);
}
return 0;
}

二、SPI(串行外设接口)

2.1 SPI功能介绍

① SPI(Serial Peripheral interface):串行外设接口,一种高速、全双工、同步的串行通信总线标准SPI的通信速率一般为1MHz~100MHz,广泛应用于EEPROM、FLASH存储器、实时时钟、AD转换器等设备,以及数字信号处理器与解码器之间的通信。该接口仅需占用芯片的四根引脚线即可实现数据传输。

② 标准SPI采用四线制组网方式

  • SCLK:时钟信号线。与I2C类似的,所有设备都需要时钟信号线进行同步,该信号由主机负责提供;
  • MOSI(Master Output Slave Input):主出从入,相当于主机的数据发送线;
  • MISO(Master Input Slave Output):主入从出,相当于主机的数据接收线;
  • CS(Slave Select/Chip Select):片选信号,与I2C不同, SPI设备没有所谓的从机地址,连接在总线上的设备通过CS片选信号选择需要进行通信的从设备。在同一时刻,总线上只有一个外设被选择, CS信号线通常是低电平有效的。
  • 注意:MOSI和MISO并非必须同时存在,可以仅保留MOSI或MISO。但这种方式仅支持单工通信,此时SPI被称为三线制。具体采用三线还是四线取决于外设需求。例如,OLCD只需要主机发送数据而无需从机返回数据,因此适合采用三线制SPI连接方式。

③ SPI的数据传输的时序

  • 时钟极性(CPOL,Clock Polarity):规定了时钟信号线在空闲时的状态, CPOL=0表示时钟信号线在空闲时为低电平; CPOL=1表示时钟信号线在空闲是为高电平;
  • 时钟相位(CPHA,Clock Phase):与I2C不同, SPI的数据接收方并不是在时钟高或者低电平时采样的,而是在时钟信号处于沿时(接收方在第几个边沿采样数据),至于是在上升沿还是下降沿取决于CPHA。 CPHA=0表示是在时钟信号变化的第一个沿时采样,CPHA=1表示是在时钟信号变化的第二个沿时采样。
  • 位序(高位先行/低位先行)未明确规定,默认高位MSB先行。
  • SPI四种工作方式:具体使用哪种方式是由外设厂家规定

        CPOL = 1,CPHA = 0: SCLK空闲为高电平, SPI下降沿采样;
        CPOL = 1,CPHA = 1: SCLK空闲为高电平, SPI上升沿采样;
        CPOL = 0,CPHA = 0: SCLK空闲为低电平, SPI上升沿采样;
        CPOL = 0,CPHA = 1: SCLK空闲为低电平, SPI下降沿采样;

    ④ SPI读写特性:写即是读, 读即是写

            SPI是全双工移位通信:读写时序可以一起完成。主从两侧各有一个移位寄存器。每个时钟沿到来时,主机通过 MOSI 往外“移出”1位,同时从 MISO “移入”1位。也就是—每“写”一位的同时必然“读”到一位。

            写即是读:你往总线“写”一个字节(移出8位),同一过程里也会“读”回从机在MISO上的8位(可能是无效/占位字节)。

            读即是写:你想从从机“读”数据,必须提供时钟,所以主机必须同时“写”出一个占位字节(通常是0xFF或0x00),用这些时钟把从机的数据“移入”。

            主机写:主机通过MOSI发送寄存器地址 -> 主机通过MOSI发送数据-> 从机通过MISO回复数据
            主机读:主机通过MOSI发送寄存器地址 -> 主机通过MOSI发送dummy(0x00 ~ 0xFF) -> 从机通过MISO回复传感器数据

    ⑤ SPI 与 I2C 的区别

    (1)接口线数

    • I2C:固定使用两根线(SCL 时钟线,SDA 数据线)
    • SPI:通常使用三线或四线(SCLK 时钟线,CS 片选线,MOSI 主出从入,MISO 主入从出)

    (2)寻址方式

    • I2C:通过七位从机地址对从机进行寻址
    • SPI:通过将对应的片选(CS)电平拉低拉选择与哪个从机进行通信

    (3)通信速率

    • I2C:标准模式100Kbps,高速模式400Kbps
    • SPI:速率可达MHz级别,通常为几十至几百MHz

    (4)通信特性

    • I2C:半双工、同步、串行通信
    • SPI:全双工、同步、串行通信

    2.2 IMX6ULL 的 SPI 模块

    ① I.MX6ULL 自带的 SPI 外设叫做 ECSPI,全称是 Enhanced Configurable Serial Peripheral Interface。 ECSPI 有 64*32 个接收FIFO(RXFIFO)和 64*32 个发送 FIFO(TXFIFO)。

    ② I.MX6ULL 的 ECSPI 可以工作在主模式或从模式,我们使用主模式, I.MX6ULL 有 4 个ECSPI,每个 ECSPI 支持四个片选信号
    ③ ECSPI 的主要特性包括:

    • 全双工同步串行接口
    • 可配置为主机/从机模式
    • 四个片选(SS)信号,支持多个外设
    • 传输继续功能允许进行无长度限制的数据传输
    • 发送和接收数据均配备 32 位宽、64 项的 FIFO(先进先出)
    • 片选(SS)和 SPI 时钟(SCLK)的极性和相位可配置
    • 支持直接内存访问(DMA)
    • 最高工作频率可达参考时钟频率

    ④ 时钟选择:从时钟树可以看出, ECSPI时钟源是pll3_sw_clk = 480MHz,经过一
    个8分频的静态分频器,实际工作频率是60MHz;

            注意:PRE_DIVIDER和POST_DIVIDER这两个分配器决定了SPI最终的时钟,由于默认情况下SPI的时钟来源于pll3_sw_clk的8分频即480M/8=60MHz,如果需要SPI时钟信号为1MHz,则可将POST_DIVIDER设置为4分频,60/4=15MHz,再将15M进行15分频,

    15MHz/15=1MHz。
    ⑤ 重点寄存器介绍

    寄存器全称作用位说明
    ECSPIx_RXDATA Receive Data Register 接受数据寄存器 ECSPI_RXDATA 32位,读取及存取 ECSPIx_RXDATA里面的数据就可以得到
    刚刚接收到的数据
     
    ECSPIx_TXDATA Transmit Data Register 发送数据寄存器 ECSPIx_TXDATA 32位,发送数据就向寄存器 ECSPIx_TXDATA 写入数据
     
    ECSPIx_CONREG Control Register 控制寄存器 BURST_LENGTH(b20-b31)

    数据长度位:每次传输一字节的话就设置为7

    CHANNEL_SELECT(b18-b19)

    通道选择位

            00:通道0

            01:通道1

            10:通道2

            11:通道3

    PRE_DIVIDER(b12-b15)

    SPI 预分频

    ECSPI 时钟频率使用两步来完成分频,此位设置的是第一步,可设置 0~15,分别对应 1~16 分频。

    POST_DIVIDER(b8-b11)

    SPI 分频值

    ECSPI 时钟频率的第二步分频设置

    分频值为 2^ POST_DIVIDER

    CHANNEL_MODE(b4-b7) SPI 通道主/从模式设置,CHANNEL_MODE[3:0]分别对应 SPI通道 3~0,为 0 的话就是设置为从模式,如果为 1 的话就是主模式。
    SMC(b3) 开始模式控制,此位只能在主模式下起作用,为 0 的话通过 XCH 位来开启 SPI突发访问,为 1 的话只要向 TXFIFO 写入数据就开启 SPI 突发访问;
    EN(b0) SPI 使能位,为 0 的话关闭 SPI,为 1 的话使能 SPI
    ECSPIx_CONFIGREG Config Register 配置寄存器 SCLK_CTL(b20-b23)

    设置 SCLK 信号线空闲状态电平

    SCLK_CTL[3:0]分别对应通道 3 ~0

            为 0 的话 SCLK 空闲状态为低电平

            为 1 的话 SCLK 空闲状态为高电平

    SS_POL(b12-b15)

    设置 SPI 片选信号极性设置

    SS_POL[3:0]分别对应通道 3~0

            为 0 的 话片选信号低电平有效

            为 1 的话片选信号高电平有效

    SCLK_POL(b4-b7) SPI 时钟信号极性设置,也就是 CPOL,SCLK_POL[3:0]分别对应通道 3~0,为 0 的话 SCLK 高电平有效(空闲的时候为低电平),为 1 的话 SCLK 低电平有效(空闲的时候为高电平);
    SCLK_PHA() SPI时钟相位设置,也就是CPHA, SCLK_PHA[3:0]分别对应通道3~0,为 0 的话串行时钟的第一个跳变沿(上升沿或下降沿)采集数据,为 1 的话串行时钟的第二个跳变沿(上升沿或下降沿)
    采集数据
    ECSPIx_STATREG Status Register 状态寄存器 RR(b3)

    RXFIFO 就绪标志位

            为 0 的话 RXFIFO 没有数据

            为 1 的话表示 RXFIFO 中 至少有一个字的数据

    TE(b0) TXFIFO 空标志位,为 0 表示 TXFIFO 中至少有一个字的数据,为 1 表示 TXFIFO为空
     

    2.3 IMX6ULL 使用 SPI 与ADXL345 通信

     ① ADXL345是一款三轴加速度传感器,主要特点如下:
            ➢ 测量范围: ±2g、 ±4g、 ±8g、 ±16g设置;1g = 9.8m/s^2
            ➢ 分辨率:最高13位(3.9mg/LSB);0 – 2^13-1

            ➢ 灵敏度:32 / (2^13 – 1) = 32/8191 = 0.0039 = 3.9mg
            ➢ 工作电压: 2.0V至3.6V;
            ➢ 低功耗:测量模式仅0.23mA,待机模式40μA;
            ➢ 核心功能: 1、三轴加速度测量(X、 Y、 Z); 2、静态重力加速度测量; 3、动态加速度(震动、运动)测量; 4、自由落体检测; 5、单击/双击检测; 6、活动/非活动监测;
            ➢ 接口支持: SPI(3线和4线模式)、 I2C数字接口
            ➢ 特色功能: 1、内置32级FIFO缓冲; 2、可编程中断功能; 3、自动休眠和唤醒功能;
            ➢ 应用场景: 1、移动设备方向检测; 2、游戏和虚拟现实控制器; 3、运动检测和计步器; 4、防盗系统的运动检测; 5、工业振动监测; 6、医疗设备姿态检测

    ② 通信时序如下图,采用四线制SPI。可见ADXL345采用的是SPI通信模式3,即CPOL=1.CPHA=1。这里需要注意一个细节, ADXL345在向寄存器写入和读取时,寄存器的最高位是不一样的,读的时候最高位为1,写的时候最高位为0。例如写数据到0x20寄存器时,先发0x20再发要写入的数据;读取时则需要先发0xA0,之后进入读取时序。

    ③ spi.c

    //spi.c
    #include "spi.h"
    #include "MCIMX6Y2.h"
    #include "core_ca7.h"
    #include "fsl_iomuxc.h"
    //SPI初始化
    void spi3_init(void)
    {
    //1.引脚复用
    IOMUXC_SetPinMux(IOMUXC_UART2_RX_DATA_ECSPI3_SCLK, 0);
    IOMUXC_SetPinMux(IOMUXC_UART2_RTS_B_ECSPI3_MISO, 0);
    IOMUXC_SetPinMux(IOMUXC_UART2_CTS_B_ECSPI3_MOSI, 0);
    IOMUXC_SetPinMux(IOMUXC_UART2_TX_DATA_GPIO1_IO20,0);
    //2.电气属性
    IOMUXC_SetPinConfig(IOMUXC_UART2_RX_DATA_ECSPI3_SCLK, 0x10B1);
    IOMUXC_SetPinConfig(IOMUXC_UART2_RTS_B_ECSPI3_MISO, 0x10B1);
    IOMUXC_SetPinConfig(IOMUXC_UART2_CTS_B_ECSPI3_MOSI, 0x10B1);
    IOMUXC_SetPinConfig(IOMUXC_UART2_TX_DATA_GPIO1_IO20, 0x10B1);
    //3.通信前,先将CS拉高
    GPIO1->GDIR |= (1 << 20);
    GPIO1->DR |= (1 << 20); //输出
    //4.配置CONREG
    ECSPI3->CONREG = 0;
    ECSPI3->CONREG |= (7 << 20) | (0x0E << 12) | (2 << 8) | (1 << 4) | (1 << 3);
    //5.配置CONFIGREG
    ECSPI3->CONFIGREG = 0;
    ECSPI3->CONFIGREG |= (1 << 20) | (1 << 4) | (1 << 0);
    //6.使能SPI
    ECSPI3->CONREG |= (1 << 0);
    }
    //SPI读写函数
    unsigned int spi3_read_write(unsigned int data)
    {
    while((ECSPI3->STATREG &= (1 << 0)) == 0); //等待数据发送完成
    ECSPI3->TXDATA = data;
    while ((ECSPI3->STATREG &= (1 << 3)) == 0); //等待数据接受完成
    return ECSPI3->RXDATA ;
    }

    ④ adxl345.c

    //adxl345.c
    #include "adxl345.h"
    #include "spi.h"
    #include "MCIMX6Y2.h"

    // adxl345写
    void adxl345_write(unsigned char reg_address, unsigned char data)
    {
    // 1. 片选拉低
    GPIO1->DR &= ~(1 << 20);
    // 2. 发送寄存器地址
    spi3_read_write(reg_address);
    // 3. 发送写入的数据
    spi3_read_write(data);
    // 4. 片选拉高
    GPIO1->DR |= (1 << 20);
    }

    // adxl345读
    unsigned char adxl345_read(unsigned char reg_address)
    {
    unsigned char ret = 0;

    // 1. 片选拉低
    GPIO1->DR &= ~(1 << 20);
    // 2. 发送寄存器地址
    spi3_read_write(reg_address | 0x80);
    // 3. 发送占位字节
    ret = spi3_read_write(0xFF);
    // 4. 片选拉高
    GPIO1->DR |= (1 << 20);

    return ret;
    }

    //adxl345初始化
    void adxl345_init(void)
    {
    //关闭中断
    adxl345_write(0x2E, 0x08);
    //满分辨率 13位
    adxl345_write(0x31, 0x0B);
    //速率25HZ
    adxl345_write(0x2C, 0x08);
    //工作模式
    adxl345_write(0x2D, 0x0B);
    }

    struct ADXL345_Data adxl345_get_value(void)
    {
    struct ADXL345_Data ret;
    //X轴数据
    ret.x = adxl345_read(0x32);
    ret.x |= adxl345_read(0x33) << 8;
    //y轴数据
    ret.y = adxl345_read(0x34);
    ret.y |= adxl345_read(0x35) << 8;
    //z轴数据
    ret.z = adxl345_read(0x36);
    ret.z |= adxl345_read(0x37) << 8;
    //加速度
    ret.xg = ret.x * 0.0039;
    ret.yg = ret.y * 0.0039;
    ret.zg = ret.z * 0.0039;
    return ret;
    }
    //adxl345.h
    #ifndef __ADXL345_H__
    #define __ADXL345_H__
    struct ADXL345_Data
    {
    short x;
    short y;
    short z;
    float xg;
    float yg;
    float zg;
    };
    extern void adxl345_write(unsigned char reg_address, unsigned char data);
    extern unsigned char adxl345_read(unsigned char reg_address);
    extern void adxl345_init(void);
    extern struct ADXL345_Data adxl345_get_value(void);
    #endif

    ⑤ main.c

    #include "MCIMX6Y2.h"
    #include "fsl_iomuxc.h"
    #include "led.h"
    #include "beep.h"
    #include "key.h"
    #include "core_ca7.h"
    #include "interrupt.h"
    #include "clock.h"
    #include "epit.h"
    #include "gpt.h"
    #include "delay.h"
    #include "uart.h"
    #include "stdio.h"
    #include "i2c.h"
    #include "lm75.h"
    #include "adc.h"
    #include "spi.h"
    #include "adxl345.h"
    int main(void)
    {
    //时钟初始化
    clock_init();
    //GIC初始化
    system_interrupt_init();
    //led初始化
    led_init();
    //蜂鸣器beep初始化
    beep_init();
    //按键初始化
    key_init();
    //EPIT初始化
    // epit_init();
    //GPT初始化
    gpt_init();
    //串口uart初始化
    uart_init();
    //I2C初始化
    i2c_init();
    //ADC初始化
    adc1_channel_init();
    //SP初始化
    spi3_init();
    led_off();
    beep_off();
    // unsigned char t = adxl345_read(0x00);
    // printf("adxl345_ID:%x\\n", t);

    while (1)
    {
    struct ADXL345_Data t = adxl345_get_value();
    // printf("x:%d y:%d z:%d\\n", t.x, t.y, t.z);
    int i, j, k;
    i = t.xg * 10;
    j = t.yg * 10;
    k = t.zg * 10;
    printf("x_accel:%d.%d y_accel:%d.%d z_accel:%d.%d\\n", i / 10, i % 10, j / 10, j % 10, k / 10, k % 10);
    delay_ms(1000);
    }
    return 0;
    }

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » ARM06-ADC(模数转换器)和 SPI(串行外设接口)
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!