简介九齐NY8系列单片机的例程合集同时提供汇编ASM与C语言两种实现覆盖该系列绝大多数型号适合正在选型或开发NY8方案的嵌入式工程师、在校学生快速上手。压缩包内共2000个文件以nysln工程文件、naprj项目文件、cb源文件、c/h代码和asm汇编为主配套s启动文件与makefile构建脚本例程按GPIO、定时器、ADC、PWM、UART、SPI等常见外设模块组织工程结构完整、便于快速定位相关代码打开即可对照学习。资源包仅4.34MB便于下载与本地归档。已有4187人学习下载常被用作NY8开发的基础参考库。借助这套例程读者可以快速理解不同外设的寄存器配置、中断处理与汇编/C混合编程思路同时通过对比两种语言版本能更高效地完成从验证到量产固件的移植也适合作为项目原型改写的起点。1. 九齐NY8例程包从点灯到量产的起步脚手架做低成本8位机方案时最怕的不是芯片手册看不懂而是手里没有能直接编译、烧录、看到现象的可运行例程。九齐NY8系列这套ASM/C双语例程正好补齐了这块短板。它把GPIO、定时器、PWM、ADC、中断、低功耗这些常用外设拆成一个个独立工程每个工程都有汇编和C两版对照适合两类人一类是刚接触NY8、想尽快点亮第一颗灯的新手另一类是从汇编往C语言迁移、需要对照寄存器配置做移植的熟手。下面按我实际拆包的顺序把资源结构、环境搭建、例程拆解和常见坑一次讲完。2. 例程包的结构与选型先看懂文件再动手2.1 例程目录与工程文件的对应关系把压缩包解开之后第一件事不是找IDE而是先建立目录索引。这套例程按外设模块分文件夹GPIO、Timer、PWM、ADC、Interrupt、Sleep、EEPROM读写等。每个文件夹下基本都是同一型号的独立工程工程文件后缀名跟具体IDE版本有关有的以.prj保存有的则是XML格式的工程描述文件但核心文件其实是.asm或.c源文件加一个头文件和工程配置。例程包根目录一般还有原理图或参考接线图我建议先不看电路图因为不同厂家做的测试板引脚定义不一定相同直接照抄会踩坑。我一般会先打开任意一个例程的工程文件确认三件事芯片型号是否与手上的样片一致、编译选项里的振荡器频率和代码选项字是否合理、烧录器型号和电压设置是否正确。例程包里通常会有一个README或Release_Notes文本把这个版本支持的外设列表和修改记录写清楚。如果没做这一步直接拿另一个型号的工程改很容易在代码选项字上翻车。注意这里说的“型号”不仅是NY8A050D和NY8A051F的区别同一颗料的不同封装或不同等级存储容量也可能不同选项字里芯片型号不匹配时烧录器会直接拒绝写入。提示拿到例程包先看Release_Notes再决定先跑哪个例程不要上来就改配置。这个习惯要延伸到后续所有例程每次复制一个工程修改前先另存到自己建的目录不要让IDE直接原地编译。因为IDE生成的一些临时文件和调试缓存会污染原来的工程等你想回到初始状态对比时已经没有干净版本了。2.2 ASM与C两套实现怎么对照看同一个外设同时提供汇编和C两种实现是这个例程包最值钱的地方。汇编版把寄存器的每一位怎么操作都摆在明面上适合用来理解芯片内部结构C语言版可读性好、改参数快适合日常做产品迭代。我推荐的学习顺序是先编译通过汇编版找到对应外设的初始化代码段和数据手册里的寄存器描述逐一对照再打开同功能的C版只看main函数和控制逻辑确认同一件事在两种语言下是怎么表达的。比如操作一个IO口输出高电平汇编版可能是BSF RA, 1C版则可能写成RA1 1或者通过宏SET_PIN(RA, 1)两者最终生成的机器码等价但写法体现两种思维习惯。汇编需要你手动画出位分布来理解读-改-写过程C则把这些细节掩盖在宏里面。初学阶段两种都编译一次用IDE的反汇编窗口看C生成了什么汇编是最快建立对应关系的方式。还有一点是寄存器头文件的差异。汇编例程里会用EQU或#define把各寄存器映射到物理地址C版则统一用sbit和结构体。对照看能发现不同例程的头文件里可能把同一个寄存器地址定义成不一样的名字这不是错误而是型号差异或版本迭代的结果。遇到这种情况优先信头文件不要改代码里的名字去“匹配”数据手册。汇编和C的移植边界往往就在头文件这一层搞清楚之后再换型号只改头文件和选项字就能跑通大部分外设。2.3 外设例程的覆盖范围与优先级从实用角度看例程覆盖面不必贪多关键看是否覆盖了产品开发的完整链路。这套例程里我按优先级排序重点关注的模块是外设模块典型应用场景例程形态GPIO点灯、按键、输出控制ASM CTimer延时、定时唤醒、计数ASM CPWM调光、电机调速ASM CADC电池电压检测、传感器采集ASM C中断外部事件唤醒、定时更新标志ASM C第五类中断通常是新手最容易卡住的地方因为中断涉及寄存器保护和标志位清除出错时现象又很隐蔽。我建议先把GPIO和Timer例程跑通再进入中断例程最后再做低功耗组合场景。这样每一步的变量都被控制在最小范围出了问题也容易定位。从一个例程跳到另一个例程时别只看两个main函数的不同还要对比它们引用的头文件和选项字配置。PWM例程通常会把定时器配成特定模式这个模式会改变定时器的中断触发方式直接把它和中断例程合并可能两边都跑不起来。例程包的作者往往把每个外设都做成“最小验证工程”目的就是让你能在干净的背景里看到单模块行为所以组合前一定要确认配置之间没有冲突。3. 开发环境与烧录流程装好环境只算第一步3.1 IDE、编译器与仿真器的选择NY8系列官方主推的IDE集成了编辑器、编译器、烧录和仿真功能入门阶段不需要单独安装编译器IDE自带的就行。但要确认IDE版本和例程包验证时使用的版本是否一致。不同版本的汇编器和C编译器对语法的宽容度和优化策略不同常见情况是例程包在某个版本下编译通过换到新版IDE后报一个“instruction not recognized”或者“syntax error”这多半不是代码有问题而是编译器做了指令集扩展。我的做法是先用例程包说明里指定的IDE版本把工程原样编译一遍确认环境变量、路径配置都没问题再决定要不要升级到新版。升级时重点看编译输出窗口里的警告警告里往往会提示某个寄存器的地址在新头文件里已经偏移或者某个中断向量号不存在。编译器选择上与生产成本有关的点在于代码密度NY8这类8位机的Flash通常只有几KC编译器生成的代码比汇编大20%到30%是常态。如果例程编译后Flash占用已经到80%以上建议这部分逻辑用汇编写或者换一个优化等级。IDE里一般有O0到O3几档优化O3的效果明显但调试信息会变少中断里变量更新的行为也可能被优化到出乎意料。量产固件建议开O2或O3并做抗干扰测试开发调试阶段用O0最稳妥。仿真器方面入门用烧录器自带的仿真功能就够不一定要单独买仿真头。像例程里这种简单外设用LED和示波器验证比仿真器更直观。3.2 工程配置芯片型号、代码选项字与电压动笔之前必须确认的工程配置有三处。第一处是芯片型号。型号选错了汇编能编过但烧录器不认芯片或者烧进去了程序跑飞。第二处是代码选项字也就是芯片的硬件配置位振荡器源用内部高频还是外部晶振、看门狗开不开、低电压复位阈值选多少、烧录引脚是否复用为普通IO。第三处是烧录电压通常需要跟着芯片的工作电压走低压芯片如果烧录用默认5V可能直接把IO拉坏也可能让代码选项字里的LVR电压设置失效。代码选项字这个坑值得单独展开。NY8的选项字不是在main函数里用代码写的而是在IDE的配置界面里勾选烧录时一并写入芯片的非易失区。例程包里的工程文件已经把推荐值预设好但如果你是新建工程只复制代码选项字就会退回默认值。默认值和例程设计要求不一致时现象通常是上电不跑、复位异常、低功耗唤醒不了。我见过一个案例对方把GPIO代码都抄对了就是没设振荡器源芯片一直靠内部低频RC跑LED每秒才闪一下他以为是延时函数写错了查了一个下午。配置项推荐值视具体型号影响时钟源内部高频 / 外部晶振主频、定时精度看门狗产品建议开启死机恢复LVR阈值按工作电压低压复位行为烧录引脚复用量产可关烧录后IO数量第三处是烧录电压。如果目标板由3.3V供电烧录器就必须配到3.3V不要用5V挡。电压不匹配轻则烧录失败重则让IO口相关寄存器进入不确定状态。3.3 烧录与验证流程烧录流程我固定成一条链路先USB连接烧录器确认IDE识别到设备再选择芯片型号导入目标hex或直接烧整个工程然后确认电源电压之后点烧录观察进度条最后拔线前把烧录器切到离线模式防止目标板复位异常。目标板的电源如果是由烧录器提供的还要确认目标板上的大电容不会让电压跌落触发LVR。烧录完成不等于程序对。我每次烧完后做的第一件事不是看现象而是测静态电流。拿万用表电流档串到电源正极如果程序原本设计是Sleep状态电流却和运行态一样大多半是代码选项字里的看门狗没有关导致芯片没法真正Sleep或者某个IO口悬空产生了漏电流路径。把静态电流量了再抓运行态电流两道数据加起来例程是否真正生效就一目了然。调试阶段建议在IDE里打开“烧录后运行”选项这样每次烧完会自动复位执行。如果没这个选项就手动断电重启。这里有个小习惯用外部电源给目标板供电时烧录器只接数据线不接电源线避免两边地电位不一致形成环路电流。注意烧录失败时先看电压和接线再看选项字最后才怀疑芯片损坏。大部分情况都是前两者。4. 例程实战GPIO点灯与定时器中断的完整拆解4.1 汇编版GPIO点灯寄存器位操作例程包里最简单的工程通常是“点亮一颗LED”。汇编版代码量很小但浓缩了NY8端口操作的核心逻辑。典型结构类似; 端口方向寄存器设为输出 MOVIA 0x00 MOVAR TRISA ; RA1输出高电平 BSF RA, 1 loop: ; 延时循环 MOVIA 0xFF MOVAR TMR0 wait: DECRSZ TMR0 GOTO wait GOTO loop这段代码里TRISA是端口方向控制寄存器写入0x00表示该端口全部作为输出。BSF指令把RA寄存器的第1位置高LED就亮了。延时部分用TMR0作循环计数器DECRSZ在自减后结果为0时跳过下一条指令实现可变延时。这里有个细节DECRSZ TMR0指令周期和结果有关被跳过的GOTO wait会让总延时曲线变成非线性所以软件延时的时间并不严格等于指令数乘以周期。对照数据手册会发现BSF操作一个位的背后有一个读-改-写过程CPU先把整个RA寄存器的当前值读出来修改指定位后再写回去。如果端口同时有输入引脚外接电容等负载读回的值可能和实际电平不一致所以例程里普遍建议端口加限流电阻不只是为了LED更是为了稳定读回电平。汇编版点灯例程里通常还会有一个清看门狗指令的调用。很多新手把它忽略直接删除这个调用结果就是程序跑一会自动复位。原因是代码选项字里开了看门狗而主循环里没有喂狗。这个细节在汇编版里特别明显因为汇编不会帮你隐藏“喂狗”这个动作C版可能封装成了CLRWDT()这样的函数反而更容易忽视。4.2 C语言版点灯延时与宏定义功能相同的C语言工程代码结构更接近产品开发习惯#include ny8.h #define LED_PIN RA1 // 定义端口位 void delay_ms(unsigned int ms) { unsigned int i, j; for (i 0; i ms; i) for (j 0; j 100; j) ; } void main(void) { TRISA1 0; // RA1设为输出 LED_PIN 1; // 输出高电平 while (1) { LED_PIN ~LED_PIN; delay_ms(500); } }C版把端口位定义成宏可读性明显提升。TRISA1 0在编译后和汇编的MOVAR TRISA等价但写起来更接近位操作。延时函数是纯软件循环没有用定时器所以延时精度依赖主频和编译器优化等级。如果编译器把内层空循环优化掉延时会大幅缩短。这是很多人用C版点灯时看到LED狂闪的原因优化等级选太高空循环被删了。更稳的做法是把软件延时里的变量设成volatile强制编译器每次循环都读写该变量。虽然这会增加几条指令但保证延时行为可预期。调试阶段我甚至会在延时函数里加一个NOP()方便用示波器测量实际周期再反推延时时间是否符合预期。C语言版的好处还体现在条件编译上。例程里经常能看到#ifdef XXX来控制不同型号的寄存器映射比如同一段PWM初始化代码经过宏切换可以支持两种引脚布局。新手第一次看到会觉得乱但这是例程包故意保留的兼容层方便你往不同型号上搬。移植时只要改宏定义不需要动逻辑代码。4.3 定时器中断分频系数与重载值计算点灯例程跑通后下一步建议拆定时器中断例程。NY8的定时器一般有预分频器和周期寄存器例程里通常用定时器产生一个周期性中断来翻转LED。C代码骨架如下volatile unsigned char flag 0; void interrupt_isr(void) { if (T0IF) { T0IF 0; // 清中断标志 flag !flag; // 翻转标志位 } } void main(void) { TRISA1 0; T0IF 0; T0IE 1; // 开Timer0中断 GIE 1; // 开总中断 TMR0 0x06; // 预装载值 T0PS 0b010; // 预分频系数 1:4 while (1) { if (flag) { LED_PIN 1; flag 0; } } }中断服务程序里先判断是哪只中断源触发这里是Timer0的中断标志T0IF。标志位必须手动清零否则中断会持续触发主循环根本没机会跑。预分频系数T0PS直接决定中断频率设成1:2还是1:16中断周期相差8倍。这一点例程注释里通常写得很清楚改的时候要连看重装载值和分频系数是不是配套只改分频不改重载频率变化不是线性。设计中断频率时先算主频。假设内部振荡器是4MHz分频1:4再算TMR0从预装载值溢出所需周期数就可以推出中断周期。例程包里一般会有一张表列出典型频率下的参数组合我建议自己用Excel重算一遍因为芯片不同批次的主频会有误差表中是个近似值产品里对时间敏感的场景要实测。参数设计时要注意TMR0是8位还是16位以及预装载值在中断发生时是否会自动重新加载。如果必须手动重载应在清标志位后先把值写入TMR0再退出中断防止写入期间又被中断插队。常见做法是进入中断后立刻关总中断写完TMR0再开保证这3条指令不被插队。5. NY8例程复现中的避坑清单烧录、唤醒与寄存器读写5.1 烧录时报“芯片不识别”实际是代码选项字不对现象例程编译通过烧录器也能找到芯片但点击烧录后提示芯片ID不匹配或无法握手。原因工程里的芯片型号和实际样片不一致或者代码选项字里选了不存在的振荡器源导致芯片上电后没有正常工作时钟。还有一种常见情况是芯片已经熔断了烧录引脚或者烧录引脚被复用成了IO口烧录器自然握手失败。解决先在IDE的工程配置页把芯片型号改成实物型号然后核对选项字尤其注意振荡器选择。改完后做一次“擦除芯片”再重新烧录。如果还是不行检查烧录器和目标板之间的接线时钟线和数据线对调同样会出现类似现象。量产板上如果引出了烧录接口务必量一下这两根线到芯片引脚之间的电阻串了0欧电阻还好串了1K电阻就会导致烧录失败。5.2 低功耗例程唤醒后程序乱跑原因在唤醒源配置现象进Sleep后能用按键唤醒但唤醒后程序不是从Sleep的下一条指令继续而是跳到了复位入口状态全丢了。原因唤醒源配置和实际电平不匹配。例程里设置的是下降沿唤醒但按键电路在按下时产生的是低电平脉冲如果引脚内部上拉没打开低电平时间太短MCU认为是不符合唤醒条件的事件直接复位。另一个常见原因是外部RC复位电路的时间常数不对唤醒瞬间立刻触发了一次上电复位。解决打开内部上拉电阻把唤醒条件改成电平触发并配合电容延长低电平时间。唤醒后第一时间读端口电平确认按键状态再决定是否执行后续动作不要一唤醒就清状态。进Sleep前也要把端口配置好特别是唤醒脚以外的IO该设成输出的设成输出免得悬空引脚产生额外漏电流。5.3 中断里改共享变量不关总中断主循环读到半新半旧的数据现象主循环里判断一个标志位去执行动作但动作时有时无甚至出现中间状态。原因标志位是在中断服务程序里用多条指令更新的比如先改高字节再改低字节主循环在两条指令之间读取了变量拿到一个拼接值。8位机里一个16位变量的读写如果被打断很容易出现这种情况。解决中断服务程序里对外层可见的变量先关总中断再修改修改完立即开总中断。主循环读取时也可以临时关中断。这个操作开销很小但能根治此类偶发问题。如果变量只是单个bit就不需要这么谨慎因为一次读-改-写周期不会被另一条单bit指令打断出半新半旧的效果。5.4 例程代码和数据手册寄存器名对不上现象打开某个外设例程里面引用的寄存器名在手上的数据手册里找不到。原因例程包编译验证所用的芯片型号和数据手册版本不对。NY8系列不同型号的寄存器布局有差异数据手册更新后寄存器名也可能调整。还有一种情况是头文件里做了条件编译不同型号映射到不同物理地址你在手册里看到的名称和头文件里的宏名不一致。解决优先以例程包自带的头文件为准不要照搬另一型号的头文件。如果手头手册确实强调寄存器改名就在例程里搜索#define或EQU语句找到新旧名称的映射关系。最稳妥的办法是直接在新版IDE里新建工程把源文件替换进去让IDE按当前头文件重新编译一遍再根据编译报错反查需要调整的宏。6. 把例程改造成自己的产品框架状态机与低功耗验证例程跑通只是开始真正实用的是把这些零散外设组合成产品逻辑。我自己的习惯是把主循环改造成一个简易状态机比如一个典型的手持设备上电初始化后进入Scan状态扫描按键改变运行模式检测到无操作进入Sleep状态中断唤醒后回到Scan状态。例程里的按键扫描、定时器定时和低功耗三块代码正好对应这三个状态拼起来只需要改main函数不需要动外设驱动部分。复合场景里最实用的技巧是功耗验证。先用万用表电流档串在电源正极测量运行态和Sleep态的电流差再用示波器抓唤醒引脚的电平变化确认按键按下后确实产生了一个满足唤醒条件的边沿。这两步做下来低功耗是否生效、唤醒是否可靠就能直观看到。需要注意测量Sleep电流时最好断开烧录器因为烧录器的供电路径会掩盖芯片的真实功耗。状态机改造时的边界条件我踩过不少比如按键进入Sleep前应该先把按键标志位清零否则唤醒后会执行上一轮残留的动作比如定时器中断里修改状态变量时要参照前面说的关中断流程再比如多个外设合并时选项字里的看门狗设置要和工作模式对齐。调试阶段我习惯先关看门狗量产前再开避免看门狗在长时间调试时频繁复位干扰验证。从那以后我每次移植例程都强制走一遍先编译不优化确认现象再逐个外设替换成自己的驱动最后做动态功耗测量。这套流程完成之前不会直接进入量产代码编写基本没再翻过车。希望帮到你。本文还有配套的精品资源点击获取