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

8088单板机NMI定时中断刷新8255动态显示

1.实现功能

8253产生定时脉冲,送到8088的nmi引脚,触发nmi中断,nmi中断中调用8255的数码管动态刷新程序。

2.8255动态刷新程序

// 显示8位数字 unsigned char digits[] = {1, 2, 3, 4, 5, 6, 7, 8}; // 要显示的数字 unsigned char bit_disp=0; void display_nmi() { // unsigned char digits[] = {1, 2, 3, 4, 5, 6, 7, 8}; // 要显示的数字 //while (1) { // 按任意键退出 //for (int i = 0; i < 8; i++) { // 设置位选 (选中当前位) outp(PORT_8255_B, ~(1 << bit_disp)); // 设置段码 outp(PORT_8255_A, ~seg_codes[digits[bit_disp]]); bit_disp++; if(bit_disp>7) {bit_disp=0;} // 延时保持显示 // delay(1); // 关闭当前位显示 (消除鬼影) // outp(PORT_8255_A, 0x00); // } } //}

3.nmi中断程序

.MODEL TINY .8086 .code ORG 100h ; COM 程序入口偏移 PUBLIC _nmi_handler ; 导出符号供C使用 extrn _main:proc extrn _display_nmi:proc start: ; 手动设置堆栈(Tiny 模式要求) mov ax, cs mov ss, ax mov sp, 05fffh ;add ax,0100h mov ds,ax ; 调用 C 主函数 call near ptr _main ; 退出到 DOS(使用 AL 中的返回码) mov ah, 4Ch ; DOS 功能:程序退出 int 21h _nmi_handler PROC NEAR push ax ; 保存寄存器 inc _nmi_count ; nmi_count++ (C变量) call _display_nmi pop ax ; 恢复寄存器 iret ; 中断返回 _nmi_handler ENDP .data EXTERN _nmi_count:WORD ; 声明外部C变量 public __acrtused __acrtused = 9876h .stack END start ; 指定入口点为 start

4.完整的c程序

#include "tiny_stdarg.h" // 使用自定义可变参数实现 #define ADR_273 0x0200 #define ADR_244 0x0400 #define LED_PORT 0x800 #define PC16550_THR 0x1f0 #define PC16550_LSR 0x1f5 ///////////////////////////////////////////////////////////////////////////////// //基本的IO操作函数 ///////////////////////////////////////////////////////////////////////////////// char str[]="Hello World! 20250531 Very Ok!!!\r\n"; //char buff[60] char cx='A'; unsigned int cs_adr=0,ds_adr=0,ss_adr=0; //////////////////////////////////////////////////////////////////////////////////// /// @brief /// @param addr /// @param data void outp(unsigned int addr, char data) // 输出一字节到I/O端口 { __asm { mov dx, addr mov al, data out dx, al } } char inp(unsigned int addr) // 从I/O端口输入一字节 { char result; __asm { mov dx, addr in al, dx mov result, al } return result; } void register_read(void) { __asm { mov ax,CS mov cs_adr,ax mov ax,DS mov ds_adr,ax mov ax,SS mov ss_adr,ax } } //////////////////////////////////////////////////////////////////////////////////// //串口发送函数 //////////////////////////////////////////////////////////////////////////////////// void uart_send(char x) { int temp; while(1) { temp=inp(PC16550_LSR); if((temp&0x20)==0x20) { break; } } outp(PC16550_THR,x); } void uart_str_send(char *p) { //int i=0; //char str1[20]="Hello World!\r\n"; //char *p; //p=str1; while(*p!='\0') { uart_send(*p); p++; } /* for(i=0;i<14;i++) { uart_send(str1[i]); } */ } /////////////////////////////////////////////////////////////////////////////////// /* sprintf()函数实现 */ /* tiny_sprintf.c */ #include "tiny_stdarg.h" static void itoa(unsigned num, int base, char *out) { char buf[6]; // 16位整数最大5位数字 + 结束符 char *p = buf; int i = 0; if (num == 0) { *out++ = '0'; *out = '\0'; return; } while (num > 0) { int r = num % base; *p++ = (r < 10) ? (r + '0') : (r - 10 + 'a'); num /= base; i++; } while (i-- > 0) { *out++ = *--p; } *out = '\0'; } int tiny_sprintf(char *buf, const char *fmt, ...) { va_list args; char *p = buf; const char *s = fmt; va_start(args, fmt); while (*s) { if (*s != '%') { *p++ = *s++; continue; } s++; switch (*s) { case 'd': { int num = va_arg(args, int); if (num < 0) { *p++ = '-'; num = -num; } itoa(num, 10, p); while (*p) p++; s++; break; } case 'x': { unsigned num = va_arg(args, unsigned); itoa(num, 16, p); while (*p) p++; s++; break; } case 's': { char *str = va_arg(args, char *); while (*str) *p++ = *str++; s++; break; } case 'c': { char c = (char)va_arg(args, int); *p++ = c; s++; break; } case '%': { *p++ = '%'; s++; break; } default: { *p++ = '%'; *p++ = *s++; break; } } } *p = '\0'; va_end(args); return p - buf; } /////////////////////////////////////////////////////////////////////////////////// //NMI 中断 ////////////////////////////////////////////////////////////////////////////////// /* NMI 计数器 */ volatile unsigned char nmi_count =10; //设置中断失量表 void set_int(unsigned char int_no, void * service_proc) { _asm { push es xor ax, ax mov es, ax mov al, int_no xor ah, ah shl ax, 1 shl ax, 1 mov si, ax mov ax, service_proc mov es:[si], ax inc si inc si mov bx, cs mov es:[si], bx pop es } } //中断处理函数 /* void _interrupt near nmi_handler(void) { nmi_count++; } */ ////////////////////////////////////////////////////////////////////////////////// //8255 ////////////////////////////////////////////////////////////////////////////////// // 定义8255端口地址 (根据原理图译码确定) #define PORT_8255_A 0x200 // PA端口地址 #define PORT_8255_B 0x201 // PB端口地址 #define PORT_8255_C 0x202 // PC端口地址 #define PORT_8255_CTRL 0x203 // 控制寄存器地址 // 数码管段码表 (共阴极) unsigned char seg_codes[] = { 0x3F, // 0 0x06, // 1 0x5B, // 2 0x4F, // 3 0x66, // 4 0x6D, // 5 0x7D, // 6 0x07, // 7 0x7F, // 8 0x6F // 9 }; // 延时函数 void delay(unsigned int ms) { for (unsigned int i = 0; i < ms; i++) { for (unsigned int j = 0; j < 100; j++) { // 空循环延时 } } } // 初始化8255 void init_8255() { // 控制字: 10000001 (0x81) // A口输出, B口输出, C口输出 outp(PORT_8255_CTRL, 0x81); } // 显示8位数字 void display_numbers() { unsigned char digits[] = {1, 2, 3, 4, 5, 6, 7, 8}; // 要显示的数字 while (1) { // 按任意键退出 for (int i = 0; i < 8; i++) { // 设置位选 (选中当前位) outp(PORT_8255_B, ~(1 << i)); // 设置段码 outp(PORT_8255_A, ~seg_codes[digits[i]]); // 延时保持显示 delay(1); // 关闭当前位显示 (消除鬼影) outp(PORT_8255_A, 0x00); } } } // 显示8位数字 unsigned char digits[] = {1, 2, 3, 4, 5, 6, 7, 8}; // 要显示的数字 unsigned char bit_disp=0; void display_nmi() { // unsigned char digits[] = {1, 2, 3, 4, 5, 6, 7, 8}; // 要显示的数字 //while (1) { // 按任意键退出 //for (int i = 0; i < 8; i++) { // 设置位选 (选中当前位) outp(PORT_8255_B, ~(1 << bit_disp)); // 设置段码 outp(PORT_8255_A, ~seg_codes[digits[bit_disp]]); bit_disp++; if(bit_disp>7) {bit_disp=0;} // 延时保持显示 // delay(1); // 关闭当前位显示 (消除鬼影) // outp(PORT_8255_A, 0x00); // } } //} ////////////////////////////////////////////////////////////////////////////////// //8253 part ////////////////////////////////////////////////////////////////////////////////// // 8253定时器端口定义 #define PORT_8253_CNT0 0x300 // 计数器0地址 #define PORT_8253_CNT1 0x301 // 计数器1地址 #define PORT_8253_CNT2 0x302 // 计数器2地址 #define PORT_8253_CTRL 0x303 // 控制寄存器地址 // 时钟频率定义 (根据原理图第4页) #define PCLK_FREQUENCY 2000000 //1193182 // 标准8253时钟频率(1.193182MHz) #define OUTPUT_FREQUENCY 500 // 目标输出频率(1kHz) // 计算计数器初值 #define COUNTER_VALUE (unsigned int)(PCLK_FREQUENCY / OUTPUT_FREQUENCY) // 初始化8253定时器 void init_8253() { // 控制字: 00110110 (0x36) // 选择计数器0 | 写入高低字节 | 模式3(方波) | 二进制计数 outp(PORT_8253_CTRL, 0x36); // 写入计数器初值 (先低字节后高字节) outp(PORT_8253_CNT0, COUNTER_VALUE & 0xFF); // 低字节 outp(PORT_8253_CNT0, (COUNTER_VALUE >> 8) & 0xFF); // 高字节 } ////////////////////////////////////////////////////////////////////////////////// //char end_flag[5]={0x55,0x55,0x55,0x55,0x55}; extern void nmi_handler(void); void main(void) /*检测按键状态并由LED发光二极管显示, 若按键闭合对应LED发光二极管点亮, 若按键断开对应LED发光二极管灭.*/ { int i=0; char buffer[80]; unsigned char key_code=0xff; // 使用安全格式化 //tiny_sprintf(buffer, "Hex: %x\n",255); // 使用安全格式化 tiny_sprintf(buffer, "Decimal: %d \n" "Hex: %x \n" "String: %s \r\n", -123, 0xABCD, "Hello"); register_read(); //set_nmi_handler(); set_int(0x02, (void *)&nmi_handler); init_8255(); init_8253(); while (1) { //char button_state; //button_state=inp(ADR_244); //int i=0; //uart_str_send(str); uart_str_send(buffer); tiny_sprintf(buffer,"******************************************\r\n"); uart_str_send(buffer); tiny_sprintf(buffer,"CS_ADR= 0X%x \r\n",cs_adr); uart_str_send(buffer); tiny_sprintf(buffer,"DS_ADR= 0X%x \r\n",ds_adr); uart_str_send(buffer); tiny_sprintf(buffer,"SS_ADR= 0X%x \r\n",ss_adr); uart_str_send(buffer); tiny_sprintf(buffer,"NMI Interrupt count=%x \r\n",nmi_count); uart_str_send(buffer); tiny_sprintf(buffer,"******************************************\r\n"); uart_str_send(buffer); key_code=inp(PORT_8255_C)&0x0f; tiny_sprintf(buffer,"Key_code= 0X%x \r\n",key_code); uart_str_send(buffer); //uart_send(cx); for(i=0;i<5000;i++); for(i=0;i<5000;i++); outp(LED_PORT, 0xff); for(i=0;i<5000;i++); for(i=0;i<5000;i++); outp(LED_PORT, 0x00); //display_numbers(); } } char end_flag[5]={0x55,0x55,0x55,0x55,0x55};

5.测试验证

预期功能实现,但是发信运行不稳定,8088会随机复位,原因不明,待查。

http://www.cnnetsun.cn/news/2059551.html

相关文章:

  • SCons构建MDK工程翻车实录:从‘No module named building’到完美运行的踩坑全指南
  • 记录生活&学习Day15深度强化学习第十六集:Advantage Actor-Critic(A2C)
  • Weka机器学习实战:鸢尾花分类完整教程
  • NVIDIA开发者课程:GPU加速AI与数据科学实战指南
  • 从密码学博士到造芯悍将:白剑的17年硬核之路
  • Real Anime Z新手教程:5分钟完成加载→输入提示→生成首图全流程
  • Vue 3 + Socket.io 实时聊天项目完整开发文档
  • Phi-3.5-mini-instruct入门必看:网页封装+参数详解+中文场景调优指南
  • Flux2-Klein-9B-True-V2多场景落地:政府宣传海报/公益广告图生成实践
  • Zotero AI插件完整指南:5分钟打造你的智能文献助手
  • 别再手动配环境了!用CMake+VS2022一键搞定PCL点云库(附完整项目模板)
  • ISG Index™显示:人工智能需求提振,亚太地区科技服务市场第一季度实现两位数增长
  • Real-Anime-Z在软件测试中的应用:自动生成UI测试用例配图
  • 民俗文化特色乡村文旅设计案例:行业运营者借鉴方向解析
  • Hyperf 成熟方案的PHP数据清洗、ETL工具链最好的库
  • 低成本改造工业表计接入LoRaWAN:EdgeBus与ThinkLink构建完整物联网闭环
  • 【阿里云/字节/SRE团队内部流出】:Docker 27资源监控9大反模式+3套压测验证脚本(限免72小时)
  • C语言:判断奇偶数
  • 【2026年华为留学生暑期实习-非AI方向(通软嵌软测试算法数据科学)-4月23日-第三题- 奖品采购】(题目+思路+JavaC++Python解析+在线测试)
  • LXC 容器网络无法正常连接问题总结与解决方案
  • HunyuanVideo-Foley问题解决:显存不足、长视频处理等实战技巧分享
  • Taiji架构:轻量级DPU虚拟化与内存管理优化
  • Windows任务栏美化终极指南:用TaskbarX打造Mac风格桌面体验
  • Phi-3.5-mini-instruct惊艳效果:中文技术术语与英文缩写双向精准映射
  • nli-MiniLM2-L6-H768一文详解:如何用该模型替代BERT进行低成本NLI任务迁移
  • RWKV7-1.5B-world双语对话教程:支持中英文社交媒体风格(emoji/缩写/口语化)
  • 从nmtui失败到命令搞定:我的Linux网络排错实战笔记(附虚拟机环境避坑点)
  • LFM2.5-1.2B-Instruct实际作品:8语言词汇表(65536)覆盖度实测报告
  • Java修饰符通关private/protected/public/default+static静态变量方法
  • VnPy量化交易框架实战指南