微机原理课程设计大综合---计数器

1.功能

8253定时器产生1KHz的脉冲,送给8259产生定时中断,中断中动态刷新8255的数码管显示,读入8255的按键,进行计数器的重置。

2.硬件

3.程序

cpp 复制代码
#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位数码管动态显示
//int ip_disp=0;
unsigned char digits[] = {1, 2, 3, 4, 5, 6, 7, 8}; // 要显示的数字
unsigned int cnt_run=0;
void disp_update(void)
{
     cnt_run++;
     digits[0]=digits[1]=digits[2]=0;
     digits[3]=cnt_run/10000%10;
     digits[4]=cnt_run/1000%10;
     digits[5]=cnt_run/100%10;
     digits[6]=cnt_run/10%10;
     digits[7]=cnt_run%10;
}
void display_intr()
{
   // unsigned char digits[] = {1, 2, 3, 4, 5, 6, 7, 8}; // 要显示的数字
    static int ip_disp=0;
    // 设置位选 (选中当前位)
            outp(PORT_8255_B, ~(1 << ip_disp));
            
            // 设置段码
            outp(PORT_8255_A, ~seg_codes[digits[ip_disp]]);
            //outp(PORT_8255_A, 0x06);
            
            // 延时保持显示
            delay(1);
            
            // 关闭当前位显示 (消除鬼影)
            //outp(PORT_8255_A, 0x00);
  ip_disp++;
  if(ip_disp>7)
     {ip_disp=0;}
}

//////////////////////////////////////////////////////////////////////////////////
//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 1193182     // 标准8253时钟频率(1.193182MHz)
//#define OUTPUT_FREQUENCY 200      // 目标输出频率(1kHz)
#define OUTPUT_FREQUENCY 180     // 目标输出频率(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);  // 高字节
}

//////////////////////////////////////////////////////////////////////////////////
//8259 part
//////////////////////////////////////////////////////////////////////////////////
#define PIC1_CMD  0x400   // 命令端口 (A0=0)
#define PIC1_DATA 0x402  // 数据端口 (A0=1)  !!!!!!!!!!

// 初始化8259(设置自动EOI)
void init_8259(void) {
    // ICW1: 边沿触发 | 单片 | 需要ICW4
    outp(PIC1_CMD, 0x17);
    
    // ICW2: 中断向量基址=20h
    outp(PIC1_DATA, 0x08);
    
    // ICW4: 8086模式 | 自动EOI (0x03)
    outp(PIC1_DATA, 0x0f);  // 关键修改:设置自动EOI模式
    
    // OCW1: 只允许IR0中断 (11111110b)
    outp(PIC1_DATA, 0xfe);
}

//////////////////////////////////////////////////////////////////////////////////


//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);
     // 使用安全格式化
     asm cli
    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);
    set_int(0x08, (void *)&nmi_handler);//8259   与NMI共用一个中断服务函数
   
    init_8255();
    init_8253();
  
    asm  cli
    init_8259();
    asm nop
    asm  sti 

   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,"8259 Interrupt count=%d    \r\n",nmi_count);
     uart_str_send(buffer);
     tiny_sprintf(buffer,"******************************************\r\n");
     uart_str_send(buffer);

     key_code=inp(PORT_8255_C)&0x0f;
     if(key_code!=0x0f)
         {cnt_run=0;}
     tiny_sprintf(buffer,"Key_code= 0X%x     \r\n",key_code);
     uart_str_send(buffer);

     //asm  int 8
     //uart_send(cx);
      //display_intr();

      
     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);
     
     disp_update();
     //display_numbers();
   }
 }
char  end_flag[5]={0x55,0x55,0x55,0x55,0x55};
相关推荐
leing1231 小时前
毕业设计推荐-校园快递代取系统
课程设计
计算机毕业编程指导师1 小时前
【计算机毕设推荐】Python+Hadoop+Spark共享单车数据可视化分析系统 毕业设计 选题推荐 毕设选题 数据分析 机器学习 数据挖掘
大数据·hadoop·python·计算机·数据挖掘·spark·课程设计
搁浅小泽3 小时前
PCBA焊点的检测方法
单片机·嵌入式硬件·可靠性工程师
Deitymoon5 小时前
STM32——PWM控制舵机
stm32·单片机·嵌入式硬件
菜鸟的日志5 小时前
【嵌入系统】嵌入式学习笔记(一)
windows·笔记·嵌入式硬件·学习·ubuntu·操作系统
电子科技圈6 小时前
四大“门派”围攻边缘及端侧AI SoC市场“光明顶”
人工智能·嵌入式硬件·mcu·物联网·网络安全·音视频·语音识别
CHINA红旗下6 小时前
串口的使用
stm32·单片机·嵌入式硬件
踏着七彩祥云的小丑7 小时前
嵌入式测试学习第2天:欧姆定律 + 功率计算 + 电路单位换算
单片机·嵌入式硬件
BT-BOX7 小时前
Multisim 14.3 安装与汉化指南(附下载链接)
嵌入式硬件·物联网