SPI接口的ADC芯片全通道纯硬件驱动——基于HAL库和TLA2518芯片
本次需要通过TI的TL2518芯片进行ADC采样。该芯片为SPI接口具有八个通道可以全部配置成AIN进行采样本次需要探究如何该如何配置才能将芯片的采样率达到最大。1.TLA2158首先要陈列一下该芯片的一些特性为节省篇幅此处只罗列最关键的特性该芯片的详细描述请查看其手册。1.1.1寄存器读写该芯片虽然是SPI接口但是数据帧格式没有完全遵守SPI的标准格式因此配置主机的SPI时CS必须选择软件控制。imageimage以上是其读写的时序下面是我选用的SPI配置这是从某开发板的例程上抄的。至于SPI的时间频率建议选大一点因为TL2518芯片SPI接口最快可以接受30MHz的SPI_CLK。void SPI2_Init(u32 datasize){SPI2_Handler.InstanceSPI2; //SPI2SPI2_Handler.Init.ModeSPI_MODE_MASTER; //设置SPI工作模式设置为主模式SPI2_Handler.Init.DirectionSPI_DIRECTION_2LINES; //设置SPI单向或者双向的数据模式:SPI设置为双线模式SPI2_Handler.Init.DataSizedatasize; //设置SPI的数据大小:寄存器读写时8bit读数据时16bitSPI2_Handler.Init.CLKPolaritySPI_POLARITY_LOW; //串行同步时钟的空闲状态为高电平SPI2_Handler.Init.CLKPhaseSPI_PHASE_1EDGE; //串行同步时钟的第二个跳变沿上升或下降数据被采样SPI2_Handler.Init.NSSSPI_NSS_SOFT; //NSS信号由硬件NSS管脚还是软件使用SSI位管理:内部NSS信号有SSI位控制SPI2_Handler.Init.BaudRatePrescalerSPI_BAUDRATEPRESCALER_256;//定义波特率预分频的值:波特率预分频值为256SPI2_Handler.Init.FirstBitSPI_FIRSTBIT_MSB; //指定数据传输从MSB位还是LSB位开始:数据传输从MSB位开始SPI2_Handler.Init.TIModeSPI_TIMODE_DISABLE; //关闭TI模式SPI2_Handler.Init.CRCCalculationSPI_CRCCALCULATION_DISABLE;//关闭硬件CRC校验SPI2_Handler.Init.CRCPolynomial7; //CRC值计算的多项式HAL_SPI_Init(SPI2_Handler);//初始化__HAL_SPI_ENABLE(SPI2_Handler); //使能SPI2// SPI2_ReadWriteByte(0Xff); //启动传输}//SPI5底层驱动时钟使能引脚配置//此函数会被HAL_SPI_Init()调用//hspi:SPI句柄void HAL_SPI_MspInit(SPI_HandleTypeDef *hspi){GPIO_InitTypeDef GPIO_Initure;__HAL_RCC_GPIOB_CLK_ENABLE(); //使能GPIOB时钟 __HAL_RCC_SPI2_CLK_ENABLE(); //使能SPI2时钟 //PB13,14,15 GPIO_Initure.PinGPIO_PIN_13|GPIO_PIN_14|GPIO_PIN_15; GPIO_Initure.ModeGPIO_MODE_AF_PP; //复用推挽输出 GPIO_Initure.PullGPIO_PULLUP; //上拉 GPIO_Initure.SpeedGPIO_SPEED_FREQ_HIGH; //快速 HAL_GPIO_Init(GPIOB,GPIO_Initure);}void SPI2_SetSpeed(u8 SPI_BaudRatePrescaler){assert_param(IS_SPI_BAUDRATE_PRESCALER(SPI_BaudRatePrescaler));//判断有效性__HAL_SPI_DISABLE(SPI2_Handler); //关闭SPISPI2_Handler.Instance-CR10XFFC7; //位3-5清零用来设置波特率SPI2_Handler.Instance-CR1|SPI_BaudRatePrescaler;//设置SPI速度__HAL_SPI_ENABLE(SPI2_Handler); //使能SPI}1.1.2数据帧格式TL2518的ADC分辨率为12bit这意味着每次仅读回一字节数据是根本不够的你必须按照半字读回但多出来的四位也不会浪费因为该芯片可以启用ID APPEND模式在每帧数据的末尾附上所采样的通道ID。至于那个16bit的数据帧则是开启了芯片过采样这会降低你的总采样率但是却能提高单次的采样分辨率。image利用ID APPEND模式我们可以在不启用CRC的前提下也能保证每次数据帧的正确性你只需要解码ID即可。以下展示一下我的芯片寄存器是如何配置的。里面的一些宏定义没有完整展示但你只要看芯片手册就能理解了建议找一下官方写的TLA2528.h头文件这样你就不要自己去定义每个寄存器了。本随笔的重点在于后面如何配置来完成纯硬件驱动SPI来达到最高采样率的ADC采样。/*************************************************写入一串字符param voidreturn voidauthor Chanlin**************************************************/static void TLA_WriteBytes(uint8_t bytes[],uint32_t size){TLA_CS 0;while(size – 0){// printf(“byte:%x\t”,bytes);TLA_SPIReadWriteByte((bytes));// bytes;}TLA_CS 1;// printf(“\r\n”);}/*************************************************完成一次寄存器写入操作param voidreturn voidauthor Chanlin**************************************************/static void TLA_WriteReg(Reg addr,Data data){// 先简单实现一下uint8_t bytes[3]; // 设置spi frame {WR_REG,addr,data}bytes[0] WR_REG;bytes[1] addr;bytes[2] data;TLA_WriteBytes(bytes,3);// delay_us(2);}/*************************************************完成一次寄存器读取操作param voidreturn voidauthor Chanlin*************************************************/static void TLA_ReadReg(Reg addr,Datadata){// 先简单实现一下uint8_t bytes[3]; // 读取数据帧 {RD_REG,addr,DUMMY};bytes[0] RD_REG;bytes[1] addr;bytes[2] DUMMY;// 写入读取帧TLA_WriteBytes(bytes,3);// 读出数据TLA_CS 0;*dataTLA_SPIReadWriteByte(DUMMY);TLA_CS 1;// 解码完成后读回数据// *dataTLA_SPIReadWriteByte(DUMMY);}// 以下是对寄存器的配置// 读写检查TLA_WriteReg(GENERAL_CFG,0x01); // soft resetdelay_ms(20); // wait for the reset completingTLA_ReadReg(GENERAL_CFG,data); // soft reset printf(GENERAL_CFG:%x\r\n,data); TLA_ReadReg(OSR_CFG,data); // soft reset printf(OSR_CFG:%x\r\n,data); TLA_ReadReg(SYSTEM_STATUS,data); printf(chip sys status:%x\r\n,data); if(data ! 0x81){ if(data 0xc1) printf(chip sequence is ongoing\r\n); else printf(Cannot access the chip\r\n); } // timing// TLA_WriteReg(OPMODE_CFG,0x0); // 默认高速时钟源如果你发现时钟不对或者想要修改// pin// TLA_WriteReg(PIN_CFG,0x00); // 全部设置为 AIN(默认)// TLA_ReadReg(PIN_CFG,data);// printf(“PIN_CFG:%x\r\n”,data);// DATA TLA_WriteReg(DATA_CFG,0x10); // 默认无debug有ID APPEND请检查此处时序设置是否正确 TLA_ReadReg(DATA_CFG,data); printf(DATA_CFG:%x\r\n,data); // mode TLA_WriteReg(AUTO_SEQ_CH_SEL,0xFF); // 默认通道全选 TLA_ReadReg(AUTO_SEQ_CH_SEL,data); printf(SEQ_CH:%x\r\n,data); TLA_WriteReg(SEQUENCE_CFG,0x11); // 默认使用auto-sequence mode且打开 TLA_ReadReg(SEQUENCE_CFG,data); printf(SEQUENCE_CFG:%x\r\n,data); TLA_CS 1;//TLA_ReadReg(PIN_CFG,data);printf(“PIN_CFG:%x\r\n”,data);// 使用manual试下// TLA_WriteReg(CHANNEL_SEL,1);// ADC offset Calib while(1){ TLA_ReadReg(GENERAL_CFG,data);// printf(“ADC offset Calib:%x\r\n”,data);if((data 1 0x1) 0 (data 2 0x01) 1) break;// 非常重要的一点是配完TLA2518的寄存器后不要忘记把主机的SPI改成16bit的数据帧格式__HAL_SPI_DISABLE(SPI2_Handler);SPI2_Handler.Init.DataSize SPI_DATASIZE_16BIT;HAL_SPI_Init(SPI2_Handler);//初始化__HAL_SPI_ENABLE(SPI2_Handler);SPI2_SetSpeed(SPI_BAUDRATEPRESCALER_2); //设置为42M时钟,高速模式1.2.1采样时间该芯片可选时钟但一般也不会在慢时钟源下运行尤其是在用于ADC模式下采样率越高越好。而该芯片最快采样率为1MHz但考虑到其有八个通道如果全开的话分配到每个通道上最快也就125KHz。image1.2.2采样通道切换模式TLA2518提供了三种通道切换模式分别是Mannual、On-the-fly和Auto-Sequence模式这里仅介绍之后会用的Auto-Sequence模式其实用on-the-fly模式也能实现。image在使用这一模式时你只需在最开始往寄存器中写好你要采样的通道在上面展示的配置中我把八个通道全开了。然后你需要达到三个条件才能让整个时序动起来并读到你想要的数据。1.控制CS引脚生成上升沿和下降沿2.控制SPI生成时钟如果你是主机的话3.从SPI-DR寄存器中读取数据到内存这样才能使用这三个条件放在一起时很容易联想到采用PWM控制CS引脚采用DMA来让SPI进行自动的收发最终实现整个时序。2.实现毫无疑问这里需要用的的片上外设资源包括一个定时器的通道要被配置成PWM两个DMA(一个触发源为TIM_CH一个触发源为SPI_RX)。以下是TIM的配置当然也是抄的例程。/***************************************************param voidreturn voidauthor Chanlin**************************************************/void TIM3_PWM_Init(u16 arr,u16 psc){TIM3_Handler.InstanceTIM3; //定时器3TIM3_Handler.Init.Prescalerpsc; //定时器分频TIM3_Handler.Init.CounterModeTIM_COUNTERMODE_UP;//向上计数模式TIM3_Handler.Init.Periodarr; //自动重装载值TIM3_Handler.Init.ClockDivisionTIM_CLOCKDIVISION_DIV1;HAL_TIM_PWM_Init(TIM3_Handler); //初始化PWMTIM3_CH4Handler.OCModeTIM_OCMODE_PWM1; //模式选择PWM1TIM3_CH4Handler.Pulsearr/2; //设置比较值,此值用来确定占空比默认比较值为自动重装载值的一半,即占空比为50%TIM3_CH4Handler.OCPolarityTIM_OCPOLARITY_HIGH; //输出比较极性为低HAL_TIM_PWM_ConfigChannel(TIM3_Handler,TIM3_CH4Handler,TIM_CHANNEL_4);//配置TIM3通道2SET_BIT(TIM3_Handler.Instance-DIER,TIM_DIER_CC4DE_Msk);HAL_TIM_PWM_Start(TIM3_Handler,TIM_CHANNEL_4);//开启PWM通道2}/***************************************************param voidreturn voidauthor Chanlin**************************************************/void HAL_TIM_PWM_MspInit(TIM_HandleTypeDef *htim){GPIO_InitTypeDef GPIO_Initure;if(htim-InstanceTIM3){__HAL_RCC_TIM3_CLK_ENABLE(); //使能定时器3// __HAL_AFIO_REMAP_TIM3_PARTIAL(); //TIM3通道引脚部分重映射使能__HAL_RCC_GPIOB_CLK_ENABLE(); //开启GPIOB时钟GPIO_Initure.PinGPIO_PIN_1; //PB1 GPIO_Initure.ModeGPIO_MODE_AF_PP; //复用推挽输出 GPIO_Initure.PullGPIO_PULLUP; //上拉 GPIO_Initure.SpeedGPIO_SPEED_FREQ_HIGH;//高速 HAL_GPIO_Init(GPIOB,GPIO_Initure);}}以下是DMA的配置这个真是我自己写的/*************************************************完成一次寄存器读取操作param voidreturn voidauthor Chanlin**************************************************/static void ConfigDMA(){__HAL_RCC_DMA1_CLK_ENABLE(); //DMA1时钟使能__HAL_LINKDMA(SPI2_Handler,hdmarx,SPIxDMA_Handler); //将DMA与SPI联系起来(发送DMA)__HAL_LINKDMA(SPI2_Handler,hdmatx,SPIxDMA_HandlerTX); //将DMA与SPI联系起来(发送DMA)//Rx DMA配置SPIxDMA_Handler.InstanceDMA1_Channel4; //通道选择SPIxDMA_Handler.Init.DirectionDMA_PERIPH_TO_MEMORY; //存储器到外设SPIxDMA_Handler.Init.PeriphIncDMA_PINC_DISABLE; //外设非增量模式SPIxDMA_Handler.Init.MemIncDMA_MINC_ENABLE; //存储器增量模式SPIxDMA_Handler.Init.PeriphDataAlignmentDMA_PDATAALIGN_HALFWORD; //外设数据长度:8位SPIxDMA_Handler.Init.MemDataAlignmentDMA_MDATAALIGN_HALFWORD; //存储器数据长度:8位SPIxDMA_Handler.Init.ModeDMA_CIRCULAR; //外设循环模式SPIxDMA_Handler.Init.PriorityDMA_PRIORITY_HIGH; //中等优先级HAL_DMA_DeInit(SPIxDMA_Handler);HAL_DMA_Init(SPIxDMA_Handler);__HAL_DMA_ENABLE(SPIxDMA_Handler);// TXSPIxDMA_HandlerTX.InstanceDMA1_Channel3; //通道选择SPIxDMA_HandlerTX.Init.DirectionDMA_MEMORY_TO_PERIPH; //存储器到外设SPIxDMA_HandlerTX.Init.PeriphIncDMA_PINC_DISABLE; //外设非增量模式SPIxDMA_HandlerTX.Init.MemIncDMA_MINC_ENABLE; //存储器增量模式SPIxDMA_HandlerTX.Init.PeriphDataAlignmentDMA_PDATAALIGN_HALFWORD; //外设数据长度:8位SPIxDMA_HandlerTX.Init.MemDataAlignmentDMA_MDATAALIGN_HALFWORD; //存储器数据长度:8位SPIxDMA_HandlerTX.Init.ModeDMA_CIRCULAR; //外设循环模式SPIxDMA_HandlerTX.Init.PriorityDMA_PRIORITY_MEDIUM; //中等优先级HAL_DMA_DeInit(SPIxDMA_HandlerTX);HAL_DMA_Init(SPIxDMA_HandlerTX);__HAL_DMA_ENABLE(SPIxDMA_HandlerTX);if (HAL_SPI_TransmitReceive_DMA(SPI2_Handler,(uint8_t*)dummy_data, // 发送缓冲区(uint8_t*)s_arrAINChannelVal, // 接收缓冲区TLA2518_CHANNEL_MAX) ! HAL_OK) {// 启动失败处理printf(“SPI DMA start failed!\r\n”);}

相关新闻

Python字符串操作全解析:从定义到常用方法

Python字符串操作全解析:从定义到常用方法

1. 定义字符串在Python中,字符串是由一系列字符组成的不可变序列,可以使用单引号()、双引号(")或三引号(或""")来定义。# 使用单引号 str1 Hello World 使用双…

2026/7/19 4:31:35阅读更多 →
HDU4348 To the moon题解(主席树区间修改模板)

HDU4348 To the moon题解(主席树区间修改模板)

此题可以作为可持久化线段树区间修改的模板题,于罗勇军老师的《算法竞赛上册》4.4 可持久化线段树的区间更新部分被简单提及了一嘴 —— 说是按标准主席树编码即可,然而并没有给出具体做法, 可能是不想限制大家的想法吧 。 但实际上也并不是直…

2026/7/19 4:31:35阅读更多 →
轻盈美学:一份专业的机构选品与合作参考指南

轻盈美学:一份专业的机构选品与合作参考指南

在轻医美行业快速迭代的今天,“轻盈美学”正成为越来越多机构和求美者共同关注的方向。它不追求过度改变,而是强调以更自然、更克制的方案,帮助求美者展现个性化的状态。但对于B端机构而言,想要搭建优质的轻盈美学项目体系&#x…

2026/7/19 4:31:35阅读更多 →
Android LiveData核心机制与最佳实践详解

Android LiveData核心机制与最佳实践详解

1. LiveData核心机制解析LiveData作为Android Jetpack架构组件中的关键成员,其设计哲学源于对Android生命周期特性的深度理解。与常规观察者模式不同,LiveData实现了真正的生命周期感知能力,这种能力通过三个核心机制实现:生命周期…

2026/7/19 8:11:58阅读更多 →
Steam Audio实战排雷:从集成到优化的五大问题与解决方案

Steam Audio实战排雷:从集成到优化的五大问题与解决方案

1. 项目概述:为什么你的音频项目总在Steam Audio上“翻车”?做游戏音频开发,尤其是涉及到空间音频和物理建模时,Steam Audio绝对是一个绕不开的名字。它免费、开源、功能强大,背后还有Valve这样的巨头支持,…

2026/7/19 8:11:58阅读更多 →
安卓逆向工程环境搭建与工具链配置指南

安卓逆向工程环境搭建与工具链配置指南

1. 安卓逆向环境搭建的必要性与核心组件 在移动安全研究、漏洞挖掘和恶意软件分析领域,安卓逆向工程是一项基础且关键的技能。不同于正向开发,逆向工程需要一套特殊的环境配置,既要具备常规安卓开发的工具链,又需要集成反编译、动…

2026/7/19 8:11:58阅读更多 →
BabelDOC:3步实现专业PDF文档的完美双语翻译

BabelDOC:3步实现专业PDF文档的完美双语翻译

BabelDOC:3步实现专业PDF文档的完美双语翻译 【免费下载链接】BabelDOC Yet Another Document Translator 项目地址: https://gitcode.com/GitHub_Trending/ba/BabelDOC 还在为PDF文档翻译后格式错乱而烦恼吗?BabelDOC是一款开源的智能文档翻译引…

2026/7/19 8:11:58阅读更多 →
大数据时代的数据收集:方法、技术与实践

大数据时代的数据收集:方法、技术与实践

1. 大数据时代的基石:数据收集入门指南当我在2015年第一次接触大数据项目时,曾天真地以为数据收集就是简单地从数据库导出CSV文件。直到面对日均TB级的日志数据时,才真正理解数据收集作为大数据流水线第一环的关键性。数据收集的质量直接决定…

2026/7/19 8:11:58阅读更多 →
考研失败后16个月技术突击:从零构建Java、前端、Python项目经验

考研失败后16个月技术突击:从零构建Java、前端、Python项目经验

对于计算机专业考研失败的同学来说,2026年就业确实面临"三无"困境:无项目经验、无实习经历、无实操能力。但这不代表没有机会,关键在于如何用剩余时间高效突击,把基础知识转化为就业竞争力。 这篇文章直接切入正题&…

2026/7/19 8:09:58阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/19 0:01:04阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/19 0:01:04阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/19 0:01:04阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/19 0:01:04阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/19 0:01:04阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/19 0:01:04阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/18 22:49:46阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/18 14:49:24阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/18 18:49:35阅读更多 →