[实例] SPI接口的ADC芯片全通道纯硬件驱动——基于HAL库和TLA2518芯片
本次需要通过TI的TL2518芯片进行ADC采样。该芯片为SPI接口具有八个通道可以全部配置成AIN进行采样本次需要探究如何该如何配置才能将芯片的采样率达到最大。1.TLA2158首先要陈列一下该芯片的一些特性为节省篇幅此处只罗列最关键的特性该芯片的详细描述请查看其手册。1.1.1寄存器读写该芯片虽然是SPI接口但是数据帧格式没有完全遵守SPI的标准格式因此配置主机的SPI时CS必须选择软件控制。以上是其读写的时序下面是我选用的SPI配置这是从某开发板的例程上抄的。至于SPI的时间频率建议选大一点因为TL2518芯片SPI接口最快可以接受30MHz的SPI_CLK。void SPI2_Init(u32 datasize) { SPI2_Handler.InstanceSPI2; //SPI2 SPI2_Handler.Init.ModeSPI_MODE_MASTER; //设置SPI工作模式设置为主模式 SPI2_Handler.Init.DirectionSPI_DIRECTION_2LINES; //设置SPI单向或者双向的数据模式:SPI设置为双线模式 SPI2_Handler.Init.DataSizedatasize; //设置SPI的数据大小:寄存器读写时8bit读数据时16bit SPI2_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;//定义波特率预分频的值:波特率预分频值为256 SPI2_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); //关闭SPI SPI2_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的数据帧则是开启了芯片过采样这会降低你的总采样率但是却能提高单次的采样分辨率。利用ID APPEND模式我们可以在不启用CRC的前提下也能保证每次数据帧的正确性你只需要解码ID即可。以下展示一下我的芯片寄存器是如何配置的。里面的一些宏定义没有完整展示但你只要看芯片手册就能理解了建议找一下官方写的TLA2528.h头文件这样你就不要自己去定义每个寄存器了。本随笔的重点在于后面如何配置来完成纯硬件驱动SPI来达到最高采样率的ADC采样。/************************************************* * 写入一串字符 * * param void * return void * author 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 void * return void * author 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 void * return void * author Chanlin **************************************************/ static void TLA_ReadReg(Reg addr,Data* data){ // 先简单实现一下 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 reset delay_ms(20); // wait for the reset completing TLA_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。1.2.2采样通道切换模式TLA2518提供了三种通道切换模式分别是Mannual、On-the-fly和Auto-Sequence模式这里仅介绍之后会用的Auto-Sequence模式其实用on-the-fly模式也能实现。在使用这一模式时你只需在最开始往寄存器中写好你要采样的通道在上面展示的配置中我把八个通道全开了。然后你需要达到三个条件才能让整个时序动起来并读到你想要的数据。1.控制CS引脚生成上升沿和下降沿2.控制SPI生成时钟如果你是主机的话3.从SPI-DR寄存器中读取数据到内存这样才能使用这三个条件放在一起时很容易联想到采用PWM控制CS引脚采用DMA来让SPI进行自动的收发最终实现整个时序。2.实现毫无疑问这里需要用的的片上外设资源包括一个定时器的通道要被配置成PWM两个DMA(一个触发源为TIM_CH一个触发源为SPI_RX)。以下是TIM的配置当然也是抄的例程。/************************************************* * * * param void * return void * author Chanlin **************************************************/ void TIM3_PWM_Init(u16 arr,u16 psc) { TIM3_Handler.InstanceTIM3; //定时器3 TIM3_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); //初始化PWM TIM3_CH4Handler.OCModeTIM_OCMODE_PWM1; //模式选择PWM1 TIM3_CH4Handler.Pulsearr/2; //设置比较值,此值用来确定占空比默认比较值为自动重装载值的一半,即占空比为50% TIM3_CH4Handler.OCPolarityTIM_OCPOLARITY_HIGH; //输出比较极性为低 HAL_TIM_PWM_ConfigChannel(TIM3_Handler,TIM3_CH4Handler,TIM_CHANNEL_4);//配置TIM3通道2 SET_BIT(TIM3_Handler.Instance-DIER,TIM_DIER_CC4DE_Msk); HAL_TIM_PWM_Start(TIM3_Handler,TIM_CHANNEL_4);//开启PWM通道2 } /************************************************* * * * param void * return void * author 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 void * return void * author 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); // TX SPIxDMA_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); }

相关新闻

Agent出现LLM因为历史工具调用消息而误解工具调用方式的问题

Agent出现LLM因为历史工具调用消息而误解工具调用方式的问题

背景 近期在Mac mini上部署了Acbox等开发者开发的Memoh——一款基于docker容器的多智能体Agent。在使用过程中出现了问题&#xff0c;Agent调用工具失败&#xff0c;并且在聊天记录中出现了类似下面格式的文本。 <tool_call ...>{...}</tool_call> 排查 在项目仓…

2026/7/6 3:49:20阅读更多 →
【全文系列目录】风控PM记

【全文系列目录】风控PM记

风控PM记 一&#xff1a;风险认知与识别&#xff08;入门篇&#xff09; ① 入门第一课&#xff1a;认识风险&#xff0c;了解风控 ② 入门第二课&#xff1a;业务催生风险&#xff0c;常见的业务风险有哪些&#xff1f; ③ 《电商风控入门&#xff1a;我们到底在“防”什…

2026/7/6 3:49:20阅读更多 →
OPENSSL生成非对称加密公私钥

OPENSSL生成非对称加密公私钥

本文内生成的文件均为密钥&#xff0c;不涉及证书的内容&#xff0c;密钥与证书的关系&#xff0c;以及各位客官所需要的究竟是密钥还是证书请自行查阅不同格式的密钥&#xff0c;使用时也会有所不同&#xff0c;因此需要明确自己要用的是什么编码和格式的密钥生成RSA公私钥生成…

2026/7/6 3:44:20阅读更多 →
WhatsApp 多账号消息路由的设计与实现

WhatsApp 多账号消息路由的设计与实现

WhatsApp 多账号消息路由的设计与实现在 WhatsApp 运营场景中&#xff0c;企业通常需要同时管理多个账号以满足不同地区、业务线或客户分群的需求。随着账号数量增加&#xff0c;消息如何被正确路由到目标账号、如何在账号间做负载均衡、以及如何处理账号异常切换&#xff0c;成…

2026/7/6 4:54:24阅读更多 →
如果你手下有人,那么这个帖子对你来说可能是很重要的。

如果你手下有人,那么这个帖子对你来说可能是很重要的。

这段时间整个部门的情况感觉一片祥和&#xff0c;距离上一次有人离职已经过去了一个多月了。其实从这个公司成立到现在&#xff0c;技术部门的人才流失率还真是相当的低的。如果不算试用期没过&#xff08;双方互相不适合&#xff09;&#xff0c;也不算那些被我们给主动劝退的…

2026/7/6 4:54:24阅读更多 →
明日方舟自动化助手:3大核心功能解放你的游戏时间

明日方舟自动化助手:3大核心功能解放你的游戏时间

明日方舟自动化助手&#xff1a;3大核心功能解放你的游戏时间 【免费下载链接】MaaAssistantArknights 《明日方舟》小助手&#xff0c;全日常一键长草&#xff01;| A one-click tool for the daily tasks of Arknights, supporting all clients. 项目地址: https://gitcode…

2026/7/6 4:54:24阅读更多 →
whisper.cpp语音识别架构决策:企业级部署的技术权衡与性能优化深度解析

whisper.cpp语音识别架构决策:企业级部署的技术权衡与性能优化深度解析

whisper.cpp语音识别架构决策&#xff1a;企业级部署的技术权衡与性能优化深度解析 【免费下载链接】whisper.cpp Port of OpenAIs Whisper model in C/C 项目地址: https://gitcode.com/GitHub_Trending/wh/whisper.cpp 在边缘计算和隐私保护日益重要的今天&#xff0c…

2026/7/6 4:54:24阅读更多 →
2026制造业数字化转型:基于工程图纸自动识别的质量检验计划实战指南

2026制造业数字化转型:基于工程图纸自动识别的质量检验计划实战指南

在 2026 年的数字化制造环境下&#xff0c;质量&#xff08;Quality&#xff09;管理早已不再局限于事后检测&#xff0c;而是贯穿于从设计图纸到成品交付的全生命周期。今天在处理一批复杂的航空级精密零件图纸时&#xff0c;再次感受到了数字化手段对提升检验计划&#xff08…

2026/7/6 4:49:24阅读更多 →
从GitHub安全案例解析常见漏洞与防护实践

从GitHub安全案例解析常见漏洞与防护实践

1. 项目概述&#xff1a;从GitHub Trending看安全实战 最近在GitHub Trending上看到一个项目&#xff0c;叫 skills4/skills &#xff0c;它因为一些安全漏洞案例被大家讨论。这其实是一个挺典型的场景&#xff1a;一个旨在展示或教授某种技能的仓库&#xff0c;本身却成了安…

2026/7/6 4:26:20阅读更多 →
MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

MLT 2026启示:因果推理与概率建模驱动下一代LLM应用

# MLT 2026启示&#xff1a;因果推理与概率建模驱动下一代LLM应用## 一、背景与挑战&#xff1a;从“黑箱预测”到“可信推理”2026年6月&#xff0c;第7届机器学习与趋势国际会议&#xff08;MLT 2026&#xff09;将在悉尼召开。会议议程中&#xff0c;“因果与可解释机器学习…

2026/7/6 2:48:33阅读更多 →
通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

通达OA SQL注入漏洞深度剖析:从手工注入到自动化利用与防御

1. 项目概述与漏洞背景最近在梳理一些历史OA系统的安全风险时&#xff0c;通达OA v11.6版本中的一个老漏洞又进入了我的视线。这个漏洞位于/general/bi_design/appcenter/report_bi.func.php文件中&#xff0c;是一个典型的SQL注入点。虽然这个漏洞的利用方式看起来并不复杂&am…

2026/7/6 0:10:35阅读更多 →
Seraphine:基于LCU API的英雄联盟智能游戏助手技术解析与应用指南

Seraphine:基于LCU API的英雄联盟智能游戏助手技术解析与应用指南

Seraphine&#xff1a;基于LCU API的英雄联盟智能游戏助手技术解析与应用指南 【免费下载链接】Seraphine 英雄联盟战绩查询工具 项目地址: https://gitcode.com/gh_mirrors/se/Seraphine 技术架构先行&#xff1a;官方接口的合规应用 你是否曾在BP阶段手忙脚乱&#x…

2026/7/6 0:03:39阅读更多 →
多协议远程连接管理工具mRemoteNG:告别混乱,统一你的远程桌面管理

多协议远程连接管理工具mRemoteNG:告别混乱,统一你的远程桌面管理

多协议远程连接管理工具mRemoteNG&#xff1a;告别混乱&#xff0c;统一你的远程桌面管理 【免费下载链接】mRemoteNG mRemoteNG is the next generation of mRemote, open source, tabbed, multi-protocol, remote connections manager. 项目地址: https://gitcode.com/gh_m…

2026/7/6 0:03:39阅读更多 →
COUNT(DISTINCT) 与 GROUP BY 去重统计:5 亿数据量下的性能实测与选型指南

COUNT(DISTINCT) 与 GROUP BY 去重统计:5 亿数据量下的性能实测与选型指南

COUNT(DISTINCT) 与 GROUP BY 去重统计&#xff1a;5 亿数据量下的性能实测与选型指南在数据分析和处理领域&#xff0c;去重统计是最基础也是最频繁使用的操作之一。当数据量达到亿级规模时&#xff0c;不同的去重统计方法在性能上可能产生天壤之别。本文将基于 5 亿行数据的实…

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

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

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

2026/7/6 4:45:01阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

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

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

2026/7/6 4:45:01阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

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

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

2026/7/6 4:45:03阅读更多 →