当前位置: 首页 > news >正文

手把手教你用STM32F103驱动TPC116S8 DAC模块(附完整工程代码)

手把手教你用STM32F103驱动TPC116S8 DAC模块(附完整工程代码)

在嵌入式开发中,数字模拟转换器(DAC)模块是实现数字信号到模拟信号转换的关键组件。TPC116S8作为一款高精度8通道DAC芯片,凭借其简单的三线制串行接口和灵活的配置选项,成为许多工程师在信号生成、工业控制等场景中的首选。本文将带领你从零开始,使用STM32F103开发板驱动TPC116S8模块,涵盖硬件连接、软件配置到实际应用的全过程。

1. 硬件准备与连接

在开始编写代码之前,正确的硬件连接是确保DAC模块正常工作的基础。TPC116S8采用标准的3线制串行接口(时钟SCLK、数据DIN和片选SYNC),与STM32F103的SPI接口兼容。

1.1 所需材料清单

  • STM32F103C8T6开发板(或其他兼容型号)
  • TPC116S8 DAC模块
  • 杜邦线若干
  • 万用表(用于验证输出电压)
  • 示波器(可选,用于观察信号波形)

1.2 引脚连接指南

TPC116S8与STM32F103的典型连接方式如下表所示:

TPC116S8引脚STM32F103引脚功能说明
DINPA15数据输入
SCLKPB3时钟信号
SYNCPB4片选信号
LDACPB5加载DAC
VDD3.3V电源正极
GNDGND电源地

提示:如果系统中需要级联多个TPC116S8模块,可以将所有模块的DIN、SCLK并联连接,然后为每个模块分配独立的SYNC和LDAC引脚。

2. 开发环境搭建

2.1 Keil MDK安装与配置

  1. 下载并安装Keil MDK-ARM开发环境(建议版本5.25或更高)
  2. 安装STM32F1系列设备支持包
  3. 新建工程,选择STM32F103C8T6作为目标设备
  4. 配置工程选项,确保勾选了"Use MicroLIB"以减小代码体积

2.2 基础驱动库准备

在工程中添加以下必要文件:

  • STM32标准外设库(STM32F10x_StdPeriph_Driver)
  • 核心支持文件(core_cm3.c, startup_stm32f10x_md.s)
  • 系统时钟配置文件(system_stm32f10x.c)
// 示例:系统时钟配置 void SystemClock_Config(void) { RCC_DeInit(); RCC_HSEConfig(RCC_HSE_ON); while(RCC_GetFlagStatus(RCC_FLAG_HSERDY) == RESET); RCC_PLLConfig(RCC_PLLSource_HSE_Div1, RCC_PLLMul_9); RCC_PLLCmd(ENABLE); while(RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET); RCC_SYSCLKConfig(RCC_SYSCLKSource_PLLCLK); while(RCC_GetSYSCLKSource() != 0x08); RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); }

3. TPC116S8驱动程序实现

3.1 GPIO初始化与宏定义

首先定义操作TPC116S8所需的GPIO控制宏,这些宏将简化后续的时序控制代码:

// TPC116S8控制引脚定义 #define ADIN(a) (a ? GPIO_SetBits(GPIOA, GPIO_Pin_15) : GPIO_ResetBits(GPIOA, GPIO_Pin_15)) #define ASCLK(a) (a ? GPIO_SetBits(GPIOB, GPIO_Pin_3) : GPIO_ResetBits(GPIOB, GPIO_Pin_3)) #define ASYNC(a) (a ? GPIO_SetBits(GPIOB, GPIO_Pin_4) : GPIO_ResetBits(GPIOB, GPIO_Pin_4)) #define LDAC(a) (a ? GPIO_SetBits(GPIOB, GPIO_Pin_5) : GPIO_ResetBits(GPIOB, GPIO_Pin_5)) void TPC116S8_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; // 使能GPIO时钟 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE); // 配置PA15(DIN)为推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_15; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置PB3(SCLK), PB4(SYNC), PB5(LDAC)为推挽输出 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_5; GPIO_Init(GPIOB, &GPIO_InitStructure); // 初始状态设置 ASYNC(1); // SYNC高电平 ASCLK(0); // SCLK低电平 LDAC(1); // LDAC高电平 }

3.2 数据发送函数实现

TPC116S8的数据传输需要严格遵循其时序要求。每个数据帧包含24位,结构如下:

  • 位23-20:无关位(可任意)
  • 位19-16:通道选择位
  • 位15-0:16位数据值
void TPC116S8_WriteData(uint8_t channel, uint16_t value) { uint32_t data = 0; uint8_t i; // 构建24位数据帧 data = ((uint32_t)(channel << 1) << 16) | value; // 开始传输 ASYNC(0); // SYNC拉低 DelayUs(1); // 发送24位数据,高位先出 for(i = 0; i < 24; i++) { ADIN((data & 0x800000) ? 1 : 0); // 设置数据位 data <<= 1; // 左移准备下一位 ASCLK(1); // 时钟上升沿 DelayUs(1); ASCLK(0); // 时钟下降沿(数据在下降沿被采样) DelayUs(1); } ASYNC(1); // SYNC拉高,结束传输 DelayUs(1); // 更新DAC输出 LDAC(0); // LDAC拉低 DelayUs(1); LDAC(1); // LDAC拉高 }

注意:DelayUs函数需要根据系统时钟频率实现微秒级延时。如果使用72MHz主频,典型的实现方式如下:

void DelayUs(uint32_t us) { us *= 72; // 72MHz下,1us需要72个周期 while(us--) { __NOP(); } }

4. 高级应用与优化

4.1 多模块级联控制

在实际应用中,可能需要同时控制多个TPC116S8模块。通过合理分配SYNC和LDAC引脚,可以实现模块的独立控制。

// 扩展的引脚定义(支持3个模块) #define LDAC1(a) (a ? GPIO_SetBits(GPIOB, GPIO_Pin_5) : GPIO_ResetBits(GPIOB, GPIO_Pin_5)) #define LDAC2(a) (a ? GPIO_SetBits(GPIOB, GPIO_Pin_7) : GPIO_ResetBits(GPIOB, GPIO_Pin_7)) #define LDAC3(a) (a ? GPIO_SetBits(GPIOB, GPIO_Pin_9) : GPIO_ResetBits(GPIOB, GPIO_Pin_9)) void TPC116S8_WriteMulti(uint8_t module, uint8_t channel, uint16_t value) { // ... 数据发送部分与单模块相同 ... // 根据模块号选择LDAC引脚 switch(module) { case 1: LDAC1(0); DelayUs(1); LDAC1(1); break; case 2: LDAC2(0); DelayUs(1); LDAC2(1); break; case 3: LDAC3(0); DelayUs(1); LDAC3(1); break; default: break; } }

4.2 输出电压校准

由于硬件差异,DAC的实际输出电压可能与理论值存在偏差。可以通过以下步骤进行校准:

  1. 设置DAC输出为最大值(0xFFFF),测量实际输出电压Vmax
  2. 设置DAC输出为最小值(0x0000),测量实际输出电压Vmin
  3. 计算校准系数:
    • 斜率 = (理论Vmax - 理论Vmin) / (Vmax实测 - Vmin实测)
    • 偏移 = 理论Vmin - (Vmin实测 × 斜率)
// 校准后的输出函数 void TPC116S8_WriteCalibrated(uint8_t channel, float voltage) { // 应用校准系数 float calibrated = (voltage - offset) / slope; // 转换为16位数值 uint16_t value = (uint16_t)(calibrated * 65535 / VREF); TPC116S8_WriteData(channel, value); }

4.3 使用DMA提高性能

对于需要高速、连续输出模拟信号的场景,可以使用DMA来减轻CPU负担:

void TPC116S8_DMA_Init(void) { DMA_InitTypeDef DMA_InitStructure; // 启用DMA时钟 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); // 配置DMA通道 DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&GPIOA->ODR; DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)waveformBuffer; DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; DMA_InitStructure.DMA_BufferSize = WAVEFORM_LENGTH; DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord; DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; DMA_InitStructure.DMA_Priority = DMA_Priority_High; DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; DMA_Init(DMA1_Channel1, &DMA_InitStructure); // 启用DMA DMA_Cmd(DMA1_Channel1, ENABLE); }

5. 常见问题排查

5.1 无输出电压

  1. 检查电源连接是否正常(VDD和GND)
  2. 确认LDAC信号是否有效(应有高低电平变化)
  3. 使用逻辑分析仪检查SCLK、DIN和SYNC信号是否符合时序要求
  4. 验证GPIO初始化是否正确,特别是复用功能配置

5.2 输出电压不稳定

  1. 检查电源滤波电容是否足够(建议在VDD附近放置0.1μF陶瓷电容)
  2. 确保GND连接良好,避免地环路干扰
  3. 在软件中添加适当的延时,确保满足TPC116S8的时序要求
  4. 检查是否有其他外设干扰GPIO操作

5.3 多模块同步问题

  1. 为每个模块分配独立的SYNC和LDAC引脚
  2. 在更新多个模块时,先发送所有数据,再同时触发LDAC
  3. 确保模块间的时钟信号同步,避免相位差导致数据错误
// 同步更新多个模块的示例 void UpdateAllModules(void) { // 发送数据到各个模块 TPC116S8_WriteDataWithoutLDAC(1, channel, value1); TPC116S8_WriteDataWithoutLDAC(2, channel, value2); TPC116S8_WriteDataWithoutLDAC(3, channel, value3); // 同时触发所有LDAC LDAC1(0); LDAC2(0); LDAC3(0); DelayUs(1); LDAC1(1); LDAC2(1); LDAC3(1); }

6. 实际应用案例

6.1 可编程电压源

利用TPC116S8的8个通道,可以构建一个多路可编程电压源:

void SetVoltageSource(uint8_t channel, float voltage) { // 确保电压在合理范围内 if(voltage < 0) voltage = 0; if(voltage > VREF) voltage = VREF; // 转换为DAC数值 uint16_t value = (uint16_t)(voltage * 65535 / VREF); // 输出到指定通道 TPC116S8_WriteData(channel, value); printf("通道%d设置为%.3fV (0x%04X)\r\n", channel, voltage, value); }

6.2 波形发生器

通过定时器中断实现基本的波形生成功能:

// 波形类型定义 typedef enum { WAVE_SINE, WAVE_SQUARE, WAVE_TRIANGLE, WAVE_SAWTOOTH } WaveformType; // 波形生成器配置 typedef struct { WaveformType type; uint16_t amplitude; // 0-65535 uint16_t frequency; // Hz uint8_t channel; } WaveGenerator; void TIM2_IRQHandler(void) { static uint16_t phase = 0; if(TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) { TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 根据波形类型计算当前值 uint16_t value; switch(currentWave.type) { case WAVE_SINE: value = currentWave.amplitude/2 * (1 + sin(2*PI*phase/256)); break; case WAVE_SQUARE: value = (phase < 128) ? currentWave.amplitude : 0; break; case WAVE_TRIANGLE: value = (phase < 128) ? (phase*2*currentWave.amplitude/256) : (currentWave.amplitude - (phase-128)*2*currentWave.amplitude/256); break; case WAVE_SAWTOOTH: value = phase * currentWave.amplitude / 256; break; } // 更新DAC输出 TPC116S8_WriteData(currentWave.channel, value); // 更新相位 phase = (phase + 1) % 256; } }

6.3 工业控制应用

在工业控制系统中,TPC116S8可用于生成控制信号:

// PID控制器输出 void PID_Output(float controlValue) { // 将PID计算结果映射到DAC范围 static const float MIN_OUTPUT = 0.0f; static const float MAX_OUTPUT = 5.0f; // 限幅处理 if(controlValue < MIN_OUTPUT) controlValue = MIN_OUTPUT; if(controlValue > MAX_OUTPUT) controlValue = MAX_OUTPUT; // 转换为DAC数值并输出 uint16_t dacValue = (uint16_t)((controlValue - MIN_OUTPUT) * 65535 / (MAX_OUTPUT - MIN_OUTPUT)); TPC116S8_WriteData(CONTROL_CHANNEL, dacValue); // 记录调试信息 DebugLog("控制输出: %.2fV -> 0x%04X", controlValue, dacValue); }
http://www.jsqmd.com/news/987600/

相关文章:

  • Redis 分布式锁进阶第六十篇
  • 别再只装系统了!惠普光影精灵2升级固态硬盘后,这样设置才能让开机速度飞起来(Win10引导分区详解)
  • FDTD/MODE仿真提速秘籍:手把手教你设置对称与反对称边界条件(附避坑指南)
  • 南宁黄金回收门店优选指南:认准正规品牌,轻松稳妥变现 - 奢侈品回收评测
  • 2026年6月上海黄金回收测评|各区门店探访,终于找到靠谱门店 - 奢侈品回收评测
  • 唐山市中级经济师工商管理/人力资源管理:适配人群、岗位匹配与备考全攻略 - 众智商学院课程中心
  • 解决CodeIgniter4中表单数据保存问题
  • 2026备考副主任护师,盘点效果出色的线上云面授班! - 医考机构品牌测评专家
  • 2026汕尾防水补漏哪家靠谱?正规公司排名及避坑价格指南 - 苏易修缮
  • 别再瞎设边界条件了!FDTD/MODE仿真提速2-8倍的对称性BC实战指南
  • 河北保冷管托厂家沧州诚嘉:全品类定制与品质保障 - 起跑123
  • 2026 佛山包包回收门店排行榜!正规商家 TOP5 最新汇总 - 奢侈品交易观察员
  • 不只是拖控件:用Qt Designer + PyUIC 高效构建你的第一个PyQt5桌面应用(附资源文件转换)
  • 2026(副)主任护师冲刺课,主流机构教学方法快速提分实力对比! - 医考机构品牌测评专家
  • 2026苏州老旧建筑修缮服务商适配报告:专业解决渗漏难题的实操指南 专业防水公司排名推荐(2026年6月防水补漏最新TOP权威排名) - 鼎壹万修缮说
  • 沈阳正规电脑回收公司排行 合规资质实测盘点 - 起跑123
  • 告别‘单车模型’:手把手教你用舵机打角计算C车模后轮差速(附测量参数)
  • LangChain 入门 服务端部署-FastAPI
  • 2026成都多门店横向测评香奈儿回收,五金掉色成色扣价标准实拍 - 奢侈品回收评测
  • 2026 无锡手表芝柏、欧米茄回收,高端复杂腕表回收 - 奢侈品回收评测
  • 告别过拟合!用迁移学习和标签平滑提升你的高光谱Transformer模型精度
  • 送男生送爸爸剃须刀排行 实用品质之选参考 - 互联网科技品牌测评
  • 2026苏州建筑防水补漏市场适配指南:苏州鼎壹万防水补漏公司及优质服务商解析 - 鼎壹万修缮说
  • 2026郑州名表回收:万国宝珀,当场打款 - 奢侈品回收评测
  • 2026最新RAG实战避坑指南:解决大模型幻觉、检索不准、上下文失效问题(附完整源码)
  • 主治医师考试高通过率的培训机构盘点 - 医考机构品牌测评专家
  • 关于CR2格式转换JPG格式的三种有效方法
  • 2026年 奔驰C/E/S车窗膜品牌推荐榜:隔热防爆与原厂级隐形守护之选 - 品牌发掘
  • 闲置名表变现,2026 无锡手表回收附件一定要带齐 - 奢侈品回收评测
  • 非泼罗尼滴剂 / 喷雾剂有效码?:瑞德医生业内优选 - 思溯深度专栏