简介这份资源面向使用 ADuC845 微控制器的嵌入式开发者尤其适合需要高精度模拟处理与混合信号控制示例的初学者和工程师。压缩包共 37 个文件约 52KB以 c 源文件与 h 头文件为主体配套 obj、lst 编译中间文件及 hex 烧录文件、uv2 工程配置覆盖 PWM、ADC、DAC、定时器、键盘扫描与 LCD12864 显示等模块便于直接导入 Keil 环境编译验证。已有 208 人学习下载。代码围绕脉宽调制的频率与占空比设置、中断初始化与服务例程、ADC 参考电压与采样时间配置展开并包含 I/O 操作、串行通信与定时器底层驱动读者可据此理解各外设的寄存器配置流程与模块间协作方式快速搭建自己的嵌入式测量与控制程序框架。1. 拿到一份 ADuC845 程序先别急着烧录翻硬盘翻出一份标着 ADuC845 的工程包很多人第一反应是找烧录器、接串口、点下载。我见过不止一个同行走这一步就卡住了代码编译能过烧进去没反应示波器一量晶振都没起振。ADuC845 这颗芯片属于 8051 内核加高精度 Σ-Δ ADC 的混合信号单片机程序能不能跑起来一半取决于代码本身另一半取决于时钟、复位、串口下载这几根线有没有接对。这份程序包适合正在做小信号采集、称重、温度变送器这类模拟前端的人也适合手上有一块老开发板、想把 8051 和 ADC 一起摸一遍的开发者。下面按「这堆文件是什么 → 怎么让它跑起来 → 怎么改成自己的」的顺序拆。2. 工程结构拆解从启动文件到 ADC 采样主循环2.1 目录里都有什么拿到一个 8051 系工程先看目录再动编译器。ADuC845 的典型工程包一般长这样不同人整理习惯不同但骨架大同小异ADuC845_Project/ ├── STARTUP.A51 # 启动代码初始化堆栈、清零 RAM ├── main.c # 主流程初始化 采样循环 ├── adc845.c / adc845.h # ADC 配置与读取封装 ├── uart.c / uart.h # 串口收发用于调试打印 ├── timer.c / timer.h # 定时器给采样定节拍 ├── lcd1602.c # 本地显示可选 ├── ADuC845.H # 寄存器定义头文件SFR 全在这里 └── project.uvproj # Keil 工程文件ADuC845.H是整个工程的地基所有特殊功能寄存器SFR的地址映射都在里面。这份头文件如果和芯片型号对不上后面全是玄学问题——比如你写ADC0CON编译器不报错但地址偏了一位配置写进去等于没写。我一般拿到工程第一件事就是打开这个头文件确认ADC0CON、ADCMODE、SFSinc 滤波器阶数这些关键寄存器的地址和手册一致。启动文件STARTUP.A51通常不用改但要知道它干了什么上电后清零内部 RAM、设置堆栈指针、跳转到main。如果你在main之前就想用全局变量得确认启动代码里的 RAM 清零范围覆盖到了你的变量区否则第一次读到的可能是随机值。2.2 主循环的骨架主程序的结构基本是「初始化 → 死循环采样 → 处理 → 输出」。下面这段是这类工程里最常见的写法我按自己的习惯加了注释#include ADuC845.H void main(void) { /* 1. 时钟与看门狗配置先关看门狗避免初始化阶段被复位 */ WDCON 0x00; // 关闭看门狗 POWCON 0x00; // 电源控制寄存器按手册默认值 /* 2. 串口初始化波特率 9600用于调试输出 */ SCON 0x50; // 串口模式1允许接收 TMOD | 0x20; // 定时器1 工作在模式28位自动重装 TH1 0xFD; // 11.0592MHz 晶振下 9600 波特率的初值 TL1 0xFD; TR1 1; // 启动定时器1 /* 3. ADC 初始化主通道、增益、滤波器阶数 */ ADC0CON 0x07; // 使能主ADC增益1单端输入 ADCMODE 0x03; // 连续转换模式启用斩波 SF 0x50; // Sinc 滤波器阶数决定输出速率与噪声 /* 4. 主循环等转换完成读结果打印 */ while (1) { while (!(ADCSTAT 0x80)); // 等待 ADC 转换完成标志位 unsigned long code ADC0DAT; // 读24位结果分三次读或一次读 printf(ADC%lu\r\n, code); // 串口输出便于观察 } }这段代码的逻辑很直白但有几个参数值得说清楚。SF寄存器控制 Sinc 滤波器的阶数阶数越高噪声越低、输出速率越慢做称重传感器时通常取 0x50 到 0xFF 之间具体看你的采样率要求。ADCMODE里的斩波位能抑制失调漂移代价是转换时间翻倍。ADC0DAT是 24 位结果寄存器8051 是 8 位机读的时候要么分三次读字节要么用编译器扩展的 32 位访问这里写成一次读是依赖 Keil 的 SFR 扩展实际移植到别的编译器要拆开。2.3 中断与采样节拍轮询等标志位在低速采样时够用但采样率一上去就会拖慢主循环。工程里如果有timer.c多半是用定时器中断来定采样节拍/* 定时器0 中断服务函数每 10ms 触发一次采样 */ void timer0_isr(void) interrupt 1 { TH0 0xDC; // 重装初值11.0592MHz 下约 10ms TL0 0x00; sample_flag 1; // 置标志主循环里处理 }中断里只置标志、不做耗时操作这是 8051 上的血泪经验。ADC 读取、串口打印这些动作放在主循环里做中断服务函数越短越好否则高优先级中断会挤掉低优先级采样节拍就乱了。TH0/TL0的重装值按晶振频率算11.0592MHz 下 10ms 定时大约是 0xDC00换成 12MHz 晶振这个值就得重算直接抄会偏。3. 跑起来编译、下载与串口验证3.1 编译环境与工程配置ADuC845 的工程基本都在 Keil C51 下编译工程文件是.uvproj。打开后先检查三处配置这三处错了编译能过但跑不起来配置项正确值错了会怎样Device 选型ADuC845或对应型号寄存器地址映射错SFR 写入无效晶振频率与实际板子一致常见 11.0592MHz串口波特率、定时器全偏输出格式HEX 文件下载器找不到可烧录文件Device 选型在 Keil 的 Options for Target → Device 里改选错型号最典型的症状是ADuC845.H里的寄存器名编译报「未定义」或者定义了但地址不对。晶振频率在 Target 选项卡里填它只影响编译器的时序计算和串口初值参考不改变实际硬件但填错了你按它算的波特率初值就会通信乱码。3.2 下载线与串口线的接法ADuC845 支持通过 UART 串口下载程序这是它比很多 8051 方便的地方不用专用仿真器。接线就三根TXD、RXD、GND加上下载时把 PSEN 或特定引脚拉低进入 bootloader 模式具体引脚看手册的下载模式章节。常见翻车点是 TXD/RXD 交叉接反——单片机的 TXD 接 USB 转串口的 RXD反过来接就收不到任何回应。下载工具一般用芯片厂商提供的串口下载软件选好 COM 口、波特率下载时通常用固定波特率比如 9600 或 19200点下载后给板子重新上电让芯片在复位时进入 bootloader。如果一直提示「等待握手」先量晶振有没有起振再确认下载模式引脚电平对不对。3.3 串口验证先让它说话程序烧进去后别急着接传感器。先让串口打印一句固定字符串确认「芯片在跑、串口通、波特率对」这三件事同时成立/* 上电后先打印一行验证最小系统 */ void uart_send_string(unsigned char *str) { while (*str) { SBUF *str; // 写入发送缓冲 while (!TI); // 等发送完成 TI 0; // 手动清标志 str; } } /* main 里初始化后调用 */ uart_send_string(ADuC845 ready\r\n);TI标志必须手动清零这是 8051 串口的固定动作忘了清第二次发送就卡死。串口助手收到这行字说明时钟、复位、串口三块都正常后面 ADC 读数不对就只需要查模拟部分排查范围一下子缩小。这一步我每次必做省下来的调试时间比写这行代码多得多。4. 核心模块拆解ADC 采样链路与寄存器配置4.1 Σ-Δ ADC 的配置顺序ADuC845 的 ADC 是 Σ-Δ 架构配置有先后顺序顺序错了转换结果就是垃圾。合理的顺序是先设ADC0CON选通道和增益再设SF定滤波器最后设ADCMODE启动转换。下面这段封装把顺序固定下来/* 初始化主 ADC通道0增益1连续转换 */ void adc_init(unsigned char channel, unsigned char gain, unsigned char sf_val) { ADC0CON 0x00; // 先清零避免残留配置 ADC0CON | (1 7); // 使能主 ADC ADC0CON | (gain 0x07); // 增益位01倍12倍... ADC0CON | (channel 0x0F) 3; // 通道选择 SF sf_val; // 滤波器阶数 ADCMODE 0x03; // 连续转换 斩波使能 }gain参数对应内部 PGA 的放大倍数做小信号时用 2 倍或 4 倍能把分辨率吃满但输入不能超过参考电压除以增益。channel选差分还是单端由ADC0CON里另一位决定这里只给了通道号实际工程里要按接线方式补上差分位。sf_val越大输出速率越低24 位结果的有效位数越高做静态称重可以往大了取做动态信号就得降下来。4.2 读 24 位结果的正确姿势24 位结果寄存器ADC0DAT在 8 位机上不能一次读干净分字节读的时候要注意顺序和符号扩展/* 读取24位ADC结果返回有符号32位 */ long adc_read(void) { unsigned char hi, mid, lo; long result; while (!(ADCSTAT 0x80)); // 等转换完成 hi ADC0DAT 16; // 高字节 mid (ADC0DAT 8) 0xFF; // 中字节 lo ADC0DAT 0xFF; // 低字节 result ((long)hi 16) | ((long)mid 8) | lo; if (result 0x800000) // 24位有符号数手动符号扩展 result | 0xFF000000; return result; }符号扩展这一步是新手最容易漏的。24 位结果最高位是符号位不扩展的话负电压读出来会变成一个巨大的正数称重传感器受压和受拉读数完全对不上。ADCSTAT的 bit7 是转换完成标志轮询它比用中断简单低速采样够用。4.3 参考电压与校准ADuC845 支持内部参考和外部参考工程里如果没显式配置默认走内部 2.5V 参考。做高精度测量时我一般用外部基准温漂小。校准分两步零点校准短接输入读偏移和满量程校准接基准电压读增益误差把两个值存到 Flash 或 EEPROM 里上电时加载。工程包里如果有calib.c多半就是干这个的没有的话自己补一个也不复杂关键是校准值要掉电保存否则每次上电都得重新校。5. 避坑与排查五个真实踩过的坑5.1 烧录后无反应晶振不起振现象下载软件提示成功但串口没有任何输出示波器测晶振引脚是平的。原因通常是晶振负载电容不匹配或者下载模式引脚在上电后没恢复。解决先换 22pF 到 33pF 的负载电容试再确认下载模式引脚在正常运行时是悬空或拉高不是一直被拉低。有些板子用跳线帽选下载模式烧完忘了拔跳线帽芯片一直停在 bootloader 里。5.2 串口能发不能收现象单片机打印正常但发命令给它没反应。原因多半是SCON里接收使能位没开或者接收中断没配。解决确认SCON 0x50里的 bit4REN为 1用中断接收的话还要开ES和总中断EA。另外检查波特率发送和接收用的是同一个定时器发送正常不代表接收端的采样点也对晶振偏差大时接收更容易出错。5.3 ADC 读数一直满量程现象不管输入接什么读出来都是 0x7FFFFF 附近。原因通常是输入引脚悬空或参考电压没接Σ-Δ ADC 输入悬空时会饱和。解决把输入短接到地看读数是否接近 0如果还是满量程查ADC0CON的通道选择位是不是选到了一个没接线的通道。还有一种情况是增益设太大小信号被放大到超过参考电压读数自然顶死。5.4 定时器中断里采样导致数据错乱现象采样值偶尔跳变规律性出现。原因是在中断里直接读 ADC 结果而 ADC 转换还没完成读到了旧数据或半更新数据。解决中断里只置标志主循环里等ADCSTAT完成位再读。如果非要在中断里读至少确认转换完成标志已置位并且读操作本身不被更高优先级中断打断。5.5 换编译器后 SFR 访问失效现象Keil 下正常换到别的 C51 编译器后 ADC 配置写不进去。原因是不同编译器对 SFR 的扩展语法不同ADC0DAT这种 24 位寄存器在 Keil 里可以当 32 位访问别的编译器可能只认 8 位。解决把 24 位读操作拆成三个字节指针访问用volatile unsigned char指向寄存器地址不依赖编译器扩展。移植性这东西平时不觉得换环境时就是后悔药。6. 二次开发把采样工程改成自己的变送器拿到这份程序多数人最终目的是把它改成自己的采集板。改造的切入点有三个换传感器接口、改通信协议、加数据处理。换传感器接口最直接adc_init里的通道和增益参数就是给你改的。称重传感器接差分通道增益取 64 或 128温度 PT100 接单端增益取 2 到 4。改完记得重做零点和满量程校准校准值存 EEPROM 的固定地址上电加载。改通信协议时把uart_send_string那套裸串口输出换成带帧头帧尾的协议帧比如0xAA 长度 数据 校验 0x55上位机好解析。发送用中断加环形缓冲别在主循环里死等TI采样率高了会丢节拍。加数据处理可以在主循环里做滑动平均或中值滤波24 位数据做 8 点滑动平均RAM 够用效果立竿见影。下面是个最小实现/* 8点滑动平均输入为 adc_read 的返回值 */ long moving_average(long new_val) { static long buf[8] {0}; static unsigned char idx 0; long sum 0; unsigned char i; buf[idx] new_val; idx (idx 1) 0x07; // 环形索引 for (i 0; i 8; i) sum buf[i]; return sum 3; // 除以8移位代替除法 }buf用static放在内部 RAM8 个long占 32 字节ADuC845 的 RAM 够。idx用位与代替取模8051 上除法慢移位快。滤波窗口大小按你的信号带宽调窗口越大越平滑但响应越慢做静态测量可以加到 16 点。验证改造是否成功我习惯用「已知输入法」接一个基准电压源从 0 到满量程取五个点看读数线性度和重复性再断电重启看校准值有没有正确加载。这套流程走一遍比在示波器上盯半天管用。从那以后我每次改这类采集工程都强制先跑一遍「串口打印 短接输入读零点」的最小验证再动传感器和协议。希望帮到你。本文还有配套的精品资源点击获取 SEO 优化官网定制响应式建站教育培训建站