STM32F407读取AD7616(CM2249)
1.AD7616介绍写在前面对本次使用AD7616做笔记记录详细特征不做记录 由于网上的资料大多收费或混乱 故重新梳理遇到的问题 希望能够帮助各位。特征CM2249 是一款16位、16通道同步采样模拟数据采集系统。各 通道均内置模拟输入钳位保护、二阶抗混叠滤波器、跟踪保持放 大器、16位 SAR ADC内置了灵活的数字滤波器、2.5V 基准电 压源、基准电压缓冲以及高速串行和并行接口。 CM2249 采用 5V 单电源供电并且可以处理 ±10V、±5V 和 ±2.5V 真双极性输入信号同时所有通道均能以高达 1MSPS 的 采样率采样。输入钳位保护电路可以承受高达 ±19.5V 的电压 1MΩ的高输入阻抗以及片内滤波器可以极大简化外围电路设 计。灵活的并行/串行接口SPI/QSPI/MICROWIRE/DSP 兼容 可选循环冗余校验 (CRC) 错误检查 8×2 通道同步采样输入本次实验使用国产CM2249芯片代替AD7616,使用SPI串行模式软件模式配置寄存器双线MISO模式进行测试。在这个模式下CM2249可以替代AD76162.硬件电路连接1.芯片需要5v和3.3V供电基本电源部分数据手册有指出。测试的输入通道有V0A~V7A和V0B~V7B共16个通道支持差分输入和单端输入如果使用单端输入需要把所有被测通道的AGND连接在一起之后通过隔离方式和DGND链接。如果使用差分输入就要像上图一样按组和外部连接。这个图就是差分输入的。AGND端口出来之后接1K电阻再连接到总的AGND。2.其他功能管脚配置。如果你需要使用基本的串行软件双线功能可以和我的一致。引脚名称 描述35 REFSEL 高电平选择内部参考37 SEQEN 低电平38,39 HW_RNGSEL1,HW_RNGSEL0 全部低电平40 SER/PAR 高电平45 DB4/SER1W 高电平选择双线输出数据55 DB10/SDI MOSI56 DB11/SDOB MISO57 DB12/SDOA MISO58,59,60 DB13/OS0 、 DB14/OS1 、 DB15/OS2 全部低电平61 WR/BURST 低电平62 SCLK/RD SCLK63 CS CS64,65,66 CHSEL0、 CHSEL1、 CHSEL2 全部低电平67 BUSY GPIO_IN68 CONVST GPIO_OUT管脚配置差不多就是这个样子详细的可以去看看数据手册下面讲讲软件协议。3.软件协议和配置1.时序分析-写寄存器的时序首先看看MOSI写寄存器的时序我使用的是软件模拟SPI。1.注意首先是RESET ,上电后首先拉低再拉高。在芯片上电完成后需使用长于 50ns 的高电平脉冲对芯片进行一次复位操 作。 RESET 保持低电平时器件将被置于关断模式。2.CONCVST 拉高再拉底我选择1ms时间3.CS拉低4.SCK, 注意时钟在空闲时应该保持高电平5.SDI/MOSI ,这里是在时钟在上升沿时将数据放在总线上注意采样时机2.时序分析-读数据的时序1.在2线模式下SDOA/SDOB一样的在SPI函数中一次读2个管脚就可以了 而读数据时序就不一样了 需要在时钟的下降沿读取就是SCLK拉低后马上读取总线上的数据。如果使用单根线读取数据就需要读取32个时钟双线只需要读取16个。3.寄存器配置寄存器配置这里我们主要配置这几个。1.0X02 /CONFIG配置寄存器可以默认直接配置0x0000所有寄存器都只有低8位有效。2.0X03 /CHANNEL通道寄存器是在转换时配置的我们要不停往总线上轮询16个通道的数据所以该寄存器要放在循环中轮询每次配置只能使能读取1个通道之后会在代码中体现。3.0X04~0X07 /RANGE量程寄存器需要在初始化时配置主要配置内容是测量的电压范围1个寄存器写4个通道范围共16个我这里直接全部配置成-10V范围寄存器的相关配置就是这些如果你需要更多功能可以看看手册。特别注意一点写寄存器时你发送的数据最高位要置为1之后的6位是寄存器地址之后的9位才是你需要配置的数据。读寄存器首位则为0。在之后的代码中每次调用: 切换通道 → 启动转换 → 等待完成 → 读取结果 → 全部转电压4.源码AD7616.C#include header_file.h /** * brief AD7616 初始化函数 * note 配置 SPI 引脚默认电平复位芯片并设置各通道量程范围 * retval 无 */ void ad7616_init(void) { /* ---------- 1. 引脚默认电平设置 ---------- */ AD7616_SCLK_H; /* SCLK 置高SPI 时钟空闲状态为高电平CPOL1 */ AD7616_CS_H; /* CS 置高片选失能SPI 总线默认空闲状态 */ AD7616_CONVST_L; /* CONVST 置低停止转换确保 ADC 处于空闲状态 */ /* ---------- 2. 硬件复位 ---------- */ HAL_Delay(1); /* 等待 1ms确保引脚电平稳定 */ ad7616_reset(); /* 硬件复位 AD7616恢复寄存器默认值 */ HAL_Delay(10); /* 等待 10ms确保复位完成后芯片进入正常工作状态 */ /* ---------- 3. 寄存器配置 (SPI 16-bit 写入) ---------- */ /* AD7616 寄存器写入格式高字节 [7]1(写), [6:1]寄存器地址, [0]0 */ /* 寄存器地址映射0x02配置寄存器, 0x03通道选择, 0x04~0x07Ch0~Ch3 量程 */ AD7616_Write_REG_and_Data(AD7616_REG_CONFIG,0x0000); /* 写配置寄存器(0x02)使用默认配置 */ AD7616_Write_REG_and_Data(AD7616_REG_CHAN_SEL,0x0000); /* 写通道寄存器(0x03)使能对应通道 */ AD7616_Write_REG_and_Data(AD7616_REG_RANGE_CH0,Range_10V); /* 写 Ch0 量程寄存器(0x04)设为 ±10V 量程 */ AD7616_Write_REG_and_Data(AD7616_REG_RANGE_CH1,Range_10V); /* 写 Ch1 量程寄存器(0x05)设为 ±10V 量程 */ AD7616_Write_REG_and_Data(AD7616_REG_RANGE_CH2,Range_10V); /* 写 Ch2 量程寄存器(0x06)设为 ±10V 量程 */ AD7616_Write_REG_and_Data(AD7616_REG_RANGE_CH3,Range_10V); /* 写 Ch3 量程寄存器(0x07)设为 ±10V 量程 */ } void ad7616_reset(void) { AD7616_RESET_H; HAL_Delay (1); AD7616_RESET_L; HAL_Delay(1); AD7616_RESET_H; } void ad7616_start_convst(void) { AD7616_CONVST_H; AD7616_CONVST_H; HAL_Delay (1); AD7616_CONVST_L; AD7616_CONVST_L; HAL_Delay (1); } /** * brief AD7616 通过 SPI 读取两路 A 组和 B 组通道转换结果 * note AD7616 为双路同时输出 ADC每个时钟脉冲同时读取 A 和 B 通道一位 * param cha_data 指针用于存储 A 通道转换结果16位有符号 * param chb_data 指针用于存储 B 通道转换结果16位有符号 * retval 无 */ void ad7616_read_data(int16_t * cha_data, int16_t * chb_data) { uint8_t i; // 循环计数器逐位读取16位数据 uint16_t rxdata_a 0; // A通道接收数据缓冲区 uint16_t rxdata_b 0; // B通道接收数据缓冲区 AD7616_CS_L; // 拉低片选信号开始 SPI 传输 for(i 0; i 16; i) // 循环读取16位从最高位到最低位逐位移入 { rxdata_a rxdata_a 1; // A通道数据左移一位准备接收新的比特位 rxdata_b rxdata_b 1; // B通道数据左移一位准备接收新的比特位 AD7616_SCLK_L; // SCLK 拉低准备读取当前位数据 // 读取 SDOA 引脚电平为高则当前比特位为 1数据缓冲区加 1 if (AD7616_D12_SDOA GPIO_PIN_SET) rxdata_a; // 读取 SDOB 引脚电平为高则当前比特位为 1数据缓冲区加 1 if (AD7616_D11_SDOB GPIO_PIN_SET) rxdata_b; AD7616_SCLK_H; // SCLK 拉高结束当前位传输 } AD7616_CS_H; // 拉高片选信号结束 SPI 传输 *cha_data rxdata_a; // 将读取结果存入传入的指针 *chb_data rxdata_b; // 将读取结果存入传入的指针 } /** * brief AD7616 通过 SPI 写入16位数据通常用于寄存器配置 * note 从最高位bit15开始逐位发送遵循 SPI 协议时序 * param txdata 要写入的16位数据高8位为地址和读写标志位低8位为数据 * retval 无 */ void ad7616_write_data(uint16_t txdata) { uint8_t i; // 循环计数器逐位发送16位数据 AD7616_CS_L; // 拉低片选信号开始 SPI 传输 for(i 0; i 16; i) // 循环发送16位从最高位到最低位 { // 判断当前最高位(bit15)并设置 SDI 引脚电平 if(txdata 0x8000) { AD7616_D10_SDI_H; // 当前位为1SDI 置高 } else { AD7616_D10_SDI_L; // 当前位为0SDI 置低 } AD7616_SCLK_L; // SCLK 拉低准备发送数据 txdata 1; // 左移一位准备发送下一个比特位 AD7616_SCLK_H; // SCLK 拉高锁存当前位数据到 AD7616 //这里数据才发出去 数据在上升沿采样 } AD7616_CS_H; // 拉高片选信号结束 SPI 传输 AD7616_D10_SDI_L; // SDI MOSI 置低恢复默认电平 } /** * brief 为了高辨识度 将write函数单独拆分成写寄存器地址和数据2个变量 * param REG : 寄存器地址 * param Data : 要写入的寄存器数据 */ void AD7616_Write_REG_and_Data(uint16_t REG,uint16_t Data) { uint16_t Together_Data; //总数据 Together_Data(AD7616_WRITE_CMD | (REG9)); //写入 写寄存器功能 寄存器地址 Together_DataTogether_Data | Data; //写入 寄存器数据 uint8_t i; // 循环计数器逐位发送16位数据 AD7616_CS_L; // 拉低片选信号开始 SPI 传输 for(i 0; i 16; i) // 循环发送16位从最高位到最低位 { // 判断当前最高位(bit15)并设置 SDI 引脚电平 if(Together_Data 0x8000) { AD7616_D10_SDI_H; // 当前位为1SDI 置高 } else { AD7616_D10_SDI_L; // 当前位为0SDI 置低 } AD7616_SCLK_L; // SCLK 拉低准备发送数据 Data 1; // 左移一位准备发送下一个比特位 AD7616_SCLK_H; // SCLK 拉高锁存当前位数据到 AD7616 //这里数据才发出去 数据在上升沿采样 } AD7616_CS_H; // 拉高片选信号结束 SPI 传输 AD7616_D10_SDI_L; // SDI MOSI 置低恢复默认电平 } /** * brief 将 ADC 原始补码数据转换为实际电压值±10V * param rawCodes : 原始 ADC 数据数组 * param voltages : 存放转换后电压的数组长度 count * param count : 要转换的通道数量 */ void AD7616_Voltage(int16_t *rawCodes, double *voltages, uint8_t count) { for(uint8_t i 0; i count; i) { int16_t signedCode (int16_t)rawCodes[i]; /* 强制转换为有符号数补码 */ /* 满量程 ±10V码值 32768 对应 10V */ voltages[i] signedCode * 10.0 / 32768.0; /* 线性换算 */ } } /** * brief AD7616 单次运行函数每次调用完成一个通道的采样 * note 每次调用: 切换通道 → 启动转换 → 等待完成 → 读取结果 → 全部转电压 * 采用读取滞后一拍机制读取的是上一轮转换的结果 * retval 无 */ void AD7616_RunData(void) { static uint8_t Channel 0; /* 当前触发转换的通道号 (0~7 循环) */ static uint8_t i 0; /* 数据存储索引指向上一轮的通道 */ static int16_t CHA_Data[8] {0}; /* A 组 8 通道原始码值有符号16位 */ static int16_t CHB_Data[8] {0}; /* B 组 8 通道原始码值有符号16位 */ static double Achannel_voltage[8]; /* A 组 8 通道转换后的电压值 */ static double Bchannel_voltage[8]; /* B 组 8 通道转换后的电压值 */ /* 1. 通道轮询切换 (0 → 1 → ... → 7 → 0 循环) */ if(Channel 7) { Channel; /* 未到末尾递增 */ } else { Channel 0; /* 已到 7回到 0 */ } /* 2. SPI 写通道选择寄存器 (0x03) */ /* 格式: 0x8600 | (Channel4) | Channel */ /* bit6~4: 偶数通道号, bit2~0: 奇数通道号 */ ad7616_write_data(0x8600 | ((Channel 0x07) 4) | (Channel 0x07)); /* 3. 启动转换 */ ad7616_start_convst(); /* CONVST 上升沿触发采样转换 */ /* 4. 等待转换完成 */ while((AD7616_BUSY GPIO_PIN_SET)) /* BUSYH → 正在转换需等待 */ { HAL_Delay(1); /* 每 1ms 轮询一次 */ } /* BUSYL → 转换完成退出 */ /* 5. 计算存储索引读取一定滞后一拍 */ /* 此时读取的是上一轮转换完成后锁存到输出寄存器的结果 */ if(Channel 0) { i 7; /* Channel0 → 上轮是 7 */ } else { i Channel - 1; /* 上轮 当前 Channel - 1 */ } /* 6. SPI 读取 A/B 两路 16 位转换结果 */ ad7616_read_data(CHA_Data[i], CHB_Data[i]); /* 7. 将 8 通道原始码值全部转为实际电压 */ AD7616_Voltage(CHA_Data, Achannel_voltage, 8); AD7616_Voltage(CHB_Data, Bchannel_voltage, 8); }AD7616.H#ifndef _AD7616_H_ #define _AD7616_H_ #include header_file.h /* AD7616 寄存器地址定义 */ #define AD7616_REG_CONFIG 0x02 /* 配置CONFIG寄存器 */ #define AD7616_REG_CHAN_SEL 0x03 /* 配置CHANNEL通道选择寄存器 */ #define AD7616_REG_RANGE_CH0 0x04 /* 配置RANGE A1/ V0A-V3A 输入范围寄存器 */ #define AD7616_REG_RANGE_CH1 0x05 /* 配置RANGE A2/ V4A-V7A 输入范围寄存器 */ #define AD7616_REG_RANGE_CH2 0x06 /* 配置RANGE A3/ V0B-V3B 输入范围寄存器 */ #define AD7616_REG_RANGE_CH3 0x07 /* 配置RANGE A4/ V4B-V7B 输入范围寄存器 */ /* SPI 读写命令标志位 */ #define AD7616_WRITE_CMD 0x8000 /* 写命令标志位 (bit15 1) */ #define AD7616_READ_CMD 0x0000 /* 读命令标志位 (bit15 0) */ /* 地址拼接宏 */ #define AD7616_WRITE(reg) (AD7616_WRITE_CMD | ((reg) 9)) #define AD7616_READ(reg) (AD7616_READ_CMD | ((reg) 9)) #define ad7616_range 10000.0 #define Range_10V 0x0000 //所有通道范围 ±10V #define Range_5V 0x00AA //偶通道 ±10V, 奇通道 ±5V #define Range_2V5 0x0055 //偶通道 ±5V, 奇通道 ±2.5V #define AD7616_RESET_L HAL_GPIO_WritePin(GPIOF, GPIO_PIN_1, GPIO_PIN_RESET) #define AD7616_RESET_H HAL_GPIO_WritePin(GPIOF, GPIO_PIN_1, GPIO_PIN_SET) #define AD7616_D4_SER1W_L HAL_GPIO_WritePin(GPIOF, GPIO_PIN_2, GPIO_PIN_RESET) #define AD7616_D4_SER1W_H HAL_GPIO_WritePin(GPIOF, GPIO_PIN_2, GPIO_PIN_SET) #define AD7616_D10_SDI_L HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_RESET) #define AD7616_D10_SDI_H HAL_GPIO_WritePin(GPIOC, GPIO_PIN_3, GPIO_PIN_SET) #define AD7616_D11_SDOB HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_4) #define AD7616_D12_SDOA HAL_GPIO_ReadPin(GPIOC, GPIO_PIN_2) #define AD7616_SCLK_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_RESET) #define AD7616_SCLK_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, GPIO_PIN_SET) #define AD7616_CS_L HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET) #define AD7616_CS_H HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET) #define AD7616_BUSY HAL_GPIO_ReadPin(GPIOF, GPIO_PIN_3) #define AD7616_CONVST_L HAL_GPIO_WritePin(GPIOF, GPIO_PIN_0, GPIO_PIN_RESET) #define AD7616_CONVST_H HAL_GPIO_WritePin(GPIOF, GPIO_PIN_0, GPIO_PIN_SET) extern void ad7616_init(void); extern void ad7616_reset(void); extern void ad7616_start_convst(void); extern void ad7616_read_data(int16_t * cha_data, int16_t * chb_data); extern void ad7616_write_data(uint16_t txdata); void AD7616_Voltage(int16_t *rawCodes, double *voltages, uint8_t count); void AD7616_Write_REG_and_Data(uint16_t REG,uint16_t Data); void AD7616_RunData(); #endifMAIN.C#include header_file.h void SystemClock_Config(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); // MX_LWIP_Init(); // MX_SPI2_Init(); //初始化ADC1 // TCP_Client_Init(); //初始化客户端 // TCP_Echo_Init(); //初始化服务器 ad7616_init(); while (1) { AD7616_RunData(); } }以上是所有内容本人也是小白可能会有错误的地方感谢指正