找回密码
 立即注册
查看: 31|回复: 1

新手学STC单片机 实验六 串并转换

[复制链接]
  • 打卡等级:偶尔看看III
  • 打卡总天数:31
  • 最近打卡:2025-12-17 09:44:38

20

主题

29

回帖

85

积分

注册会员

积分
85
发表于 5 天前 | 显示全部楼层 |阅读模式
实验目的
    掌握 HC595 串并转换的工作原理。
    掌握单片机串并转换的编程方法。

先上电路
截图202512121329548089.jpg

HC595 串并转换原理

串并转换的原理图如图 6-1 所示,本实验中使用 U21 一片 595 控制数码管的段
选。下面介绍一下 SN74HC595 芯片的使用。595(简称)包括一个八位串行输入、
并行输出的移位寄存器以及一个八位 D 型存储器。移位寄存器有一个串行移位输入
(SER)和一个串行输出(QH’)以及一个异步的低电平复位,存储寄存器有一个 8 位
并行的、具备三态的总线输出,当使能 OE 时(为低电平),存储寄存器的数据输出
到总线。这两个寄存器都有各自独立的时钟线控制。其真值表如图 6-2 所示,SRCLK
为移位寄存器的时钟线,上升沿时 SER 中的数据串入到移位寄存器中,RCLK 为存储
寄存器的时钟线,上升沿时将移位寄存器的数据锁存到存储寄存器中并从 8 位并行
输出端 QH-QA 输出。这样通过 3 个 IO 口对 595 的控制就可以间接控制数码管的 8
位段选端,节省了 IO 的开销。
截图202512121330482012.jpg

了解了 SN74HC595 的使用方法,就可以编写简单的测试程序,串出一个 8 位的
数据。具体程序如下:
/*******************************************************
//函数名称:HC595_send_byte_r()
//函数功能:行方向上的HC595串出一个字节,从高位到低位串
//入口参数:dat:串入的字节
//返回参数:无
********************************************************/
void HC595_send_byte_r(uchar dat)
{
    uchar i;
    for (i=0;i<8;i++)
    {
        HC595_data_r = dat & 0x80; //从高位到低位串出
        dat<<=1;
        HC595_clk_r = 0; //HC595_clk_r上升沿将SER数据逐步传入到移位寄存器中
        HC595_clk_r = 1;
    }
}

实验测试
连线:用杜邦线将 JP27 P00-P07 连接 JP82 SL0-SL7
JP18 QH-QA 连接 JP81 DP-A
P10-P12 连接 JP16 SER3,SRCLK,RCLK
实验现象:数码管循环显示 0-F。

代码分析

/*******************************************************
* 程序功能:串并转换
* 接线说明:JP27 P00-P07 连接 JP82 SL0-SL7
*           JP18 QH-QA 连接 JP81 DP-A
*           P10-P12 连接 JP16 SER3, SRCLK, RCLK
* 实验现象:数码管循环显示0-F
* 日    期:2014/10/30
* 作    者:
*******************************************************/

#include "hal.h"
#include "display_io.h"

#define PosPort P0

// 全局变量定义
uchar dis_1ms_ok = 0;          // 一列扫描时间到标志位
uchar dis_buff[8];             // 显示缓冲区(存储8位数码管的段码)
// 数码管字形表,供显示时查询(共阴数码管,0-F + 消隐)
uchar code disptable[17] = {
    // 定义表格需加code,存储在程序存储区(ROM)
    0x3F,  // "0": 0B00111111
    0x06,  // "1": 0B00000110
    0x5B,  // "2": 0B01011011
    0x4F,  // "3": 0B01001111
    0x66,  // "4": 0B01100110
    0x6D,  // "5": 0B01101101
    0x7D,  // "6": 0B01111101
    0x07,  // "7": 0B00000111
    0x7F,  // "8": 0B01111111
    0x6F,  // "9": 0B01101111
    0x77,  // "A": 0B01110111
    0x7C,  // "b": 0B01111100
    0x39,  // "C": 0B00111001
    0x5E,  // "d": 0B01011110
    0x79,  // "E": 0B01111001
    0x71,  // "F": 0B01110001
    0x00   // 全灭消隐
};

// 共阴数码管位选编码(低电平有效,依次选中第1-8位)
uchar code Position[] = {
    0xFE, 0xFD, 0xFB, 0xF7, 0xEF, 0xDF, 0xBF, 0x7F
};


/*******************************************************
* 函数名称:Timer0Init()
* 函数功能:定时器0初始化函数(1毫秒@12MHz)
* 入口参数:无
* 返回参数:无
********************************************************/
void Timer0Init(void)
{
    TIMER_CLK_DIV(0, 12);      // 设置定时器0时钟为12T模式
    TIMER_TIME(0);             // 设置定时器0为定时模式
    TIMER_MODE(0, 0);          // 设置定时器0工作模式0(13位定时器)
    TL0 = 0x18;                // 设置定时初值低8位
    TH0 = 0xFC;                // 设置定时初值高8位(12MHz下,1ms定时初值:FC18H)
    CLR_TIMER_FLAG(0);         // 清除定时器0溢出标志位TF0
}


/*******************************************************
* 函数名称:Timer0_ISR()
* 函数功能:定时器0中断服务程序(每1ms触发一次)
* 入口参数:无
* 返回参数:无
********************************************************/
void Timer0_ISR() interrupt T0_VECTOR using 1
{
    dis_1ms_ok = 1;            // 置位1ms扫描标志位(通知主循环执行显示)
}


/*******************************************************
* 函数名称:HC595_send_byte_r()
* 函数功能:向74HC595串行发送一个字节(从高位到低位)
* 入口参数:dat - 待发送的字节数据
* 返回参数:无
********************************************************/
void HC595_send_byte_r(uchar dat)
{
    uchar i;
    for (i = 0; i < 8; i++) {
        HC595_data_r = dat & 0x80;  // 取当前最高位(0x80=10000000B),输出到串行数据引脚
        dat <<= 1;                  // 左移1位,准备发送下一位
        HC595_clk_r = 0;            // 拉低移位时钟(准备上升沿)
        HC595_clk_r = 1;            // 上升沿:将数据移入595移位寄存器
    }
}


/*******************************************************
* 函数名称:display_io()
* 函数功能:数码管动态扫描显示(每1ms扫描1位,8位循环)
* 入口参数:无
* 返回参数:无
********************************************************/
void display_io(void)
{
    static uchar dos = 0;       // 静态变量:扫描位计数(0-7,对应8位数码管)

    PosPort = 0xFF;             // 位选端口全高,消隐(避免切换时的鬼影)
    HC595_send_byte_r(dis_buff[dos]);  // 串行发送当前位的段码(从显示缓冲区读取)

    HC595_latch_r = 0;          // 拉低锁存时钟(准备上升沿)
    HC595_latch_r = 1;          // 上升沿:将移位寄存器的数据锁存到存储寄存器(并行输出)

    PosPort = Position[dos];    // 输出位选信号,选中当前数码管(低电平有效)

    if (++dos >= 8) {           // 扫描计数递增,完成8位后归零(循环扫描)
        dos = 0;
    }
}


/*******************************************************
* 函数名称:main()
* 函数功能:程序主函数(初始化+循环调度)
********************************************************/
void main(void)
{
    uchar i, k = 0;             // i:循环变量;k:字形表索引(0-16对应0-F+消隐)
    uint msecond = 0;           // 毫秒计数器(累计1秒)

    // 初始化定时器0
    Timer0Init();
    TIMER_RUN(0, START);        // 启动定时器0
    TIMER_INT_EN(0, ON);        // 使能定时器0中断
    INT_GLOBAL_ENABLE(ON);      // 开启全局中断

    // 初始化显示缓冲区:所有数码管默认显示"0"
    for (i = 0; i < 8; i++) {
        dis_buff = disptable[0];
    }

    // 主循环(事件驱动:等待1ms扫描标志)
    while (1) {
        if (dis_1ms_ok) {        // 检测到1ms扫描时间到
            dis_1ms_ok = 0;      // 清除标志位(避免重复处理)

            display_io();        // 执行数码管扫描显示(更新当前位)

            // 累计1秒后,更新显示缓冲区(循环切换显示内容)
            if (++msecond >= 1000) {
                msecond = 0;      // 重置毫秒计数器

                // 所有数码管显示同一字符(k对应的字形)
                for (i = 0; i < 8; i++) {
                    dis_buff = disptable[k];
                }

                // 字形索引递增(0→1→...→15(F)→16(消隐)→0循环)
                if (++k > 0x10) {  // 0x10=16(消隐索引)
                    k = 0;
                }
            }
        }
    }
}

数码管底层扫描驱动
数码管的扫描底层驱动就是不考虑显示缓冲区是什么内容,当定时器 1ms 一到,
就将当前缓冲区中相应的段码显示出来。这种设计可以将底层驱动与上层程序控制
分开,更好的实现模块化,调试起来也比较方便。部分程序如下所示:

/*******************************************************
//函数名称:void display_io(void)
//函数功能:数码管1ms扫描一位
//入口参数:无
//返回参数:无
********************************************************/
void display_io(void)
{
    static uchar dos;                                   // 8位扫描计数
    PosPort = 0xff;                                     // 消隐
    HC595_send_byte_r(dis_buff[dos]);                   // 串出段数据,段值
    HC595_latch_r = 0;                                  // HC595_latch_r上升沿将并出的数据锁存到存储寄存器中
    HC595_latch_r = 1;
    PosPort = Position[dos];                            // 送位选
    if (++dos >= 8) dos = 0;                            // 扫描完8位,回到第一位
}
程序相关宏文件:

// 全局中断控制:EA=(!!on),on非0则开总中断
#define INT_GLOBAL_ENABLE(on) EA=(!!on)


/********************************************************
// 功能:定时器运行操作控制
// 参数:num 计数器号(0-4) ;on 运行控制,1:启动,0停止
*********************************************************/
#define TIMER_RUN(num,on) \
st( \
    if(num==0) \
        TR0=on; \
    else if(num==1) \
        TR1=on; \
    else if(num==2) \
        (on)?(AUXR |=BIT4):(AUXR &= ~BIT4); \
    else if(num==3) \
        (on)? (T4T3M |= BIT3):(T4T3M &=~BIT3);\
    else if(num==4) \
        (on)? (T4T3M|= BIT4):(T4T3M &=~BIT4);\
)


/********************************************************
// 功能:定时器定时操作控制
// 参数:num 计数器号(0-4)
*********************************************************/
#define TIMER_TIME(num)\
st( \
    if(num==0) \
        TMOD |=~BIT2; \
    else if(num==1) \
        TMOD |=~BIT6; \
    else if(num==2) \
        AUXR |=~BIT3; \
    else if(num==3) \
        T4T3M |= ~BIT2; \
    else if(num==4) \
        T4T3M |= ~BIT6;)


/********************************************************
// 功能:定时器中断使能操作控制
// 参数:num 计数器号(0-4) ;on 使能控制,1:允许,0禁止 //1溢出中断允许
*********************************************************/
#define TIMER_INT_EN(num,on) \
    if(num==0) \
        ET0=on; \
    else if(num==1) \
        ET1=on; \
    else if(num==2) \
        (on) ? (IE2 |= BIT3) : (IE2 &= ~BIT3); \
    else if(num==3) \
        (on) ? (IE2 |= BIT5) : (IE2 &= ~BIT5); \
    else if(num==4) \
        (on) ? (IE2 |= BIT6) : (IE2&= ~BIT6);


/********************************************************
// 功能:定时器工作模式选择
// 参数:num 计数器号(0-1) ;sel模式选择(0-3)0:16位重载 1:16位计数器 2:8位重载 3:计数器无效
*********************************************************/
#define TIMER_MODE(num,sel) \
    if(sel<4) \
    { \
        if(num==0) \
        { \
            TMOD &= ~0x03; \
            TMOD |=sel; \
        } \
        else if(num==1) \
        { \
            TMOD &= ~0x30; \
            TMOD |=sel<<3; \
        } \
    }


/********************************************************
// 功能:定时器定时时间分频选择
// 参数:num 计数器号(0-4); sel分频选择; 1:1分频,0-12分频
*********************************************************/
#define TIMER_CLK_DIV(num,sel) \
st( \
    if(num==0) \
        (sel==1) ? (AUXR |= BIT7) :(AUXR &= ~BIT7); \
    else if(num==1) \
        (sel==1) ? (AUXR |= BIT6) :(AUXR &= ~BIT6); \
    else if(num==2) \
        (sel==1) ? (AUXR |= BIT2) :(AUXR &= ~BIT2); \
    else if(num==3) \
        (sel==1) ? (T4T3M |= BIT1) :(T4T3M &= ~BIT1); \
    else if(num==4) \
        (sel==1) ? (T4T3M |= BIT5) :(T4T3M &= ~BIT5);)


/*************************************
// 功能:清除定时器中断标志 //清除溢出中断
// 参数:num 计数器号(0-1)
*********************************************************/
#define CLR_TIMER_FLAG(num) \
st( \
    if(num==0) \
        TF0=0; \
    else if(num==1) \
        TF0=0; \
)

回复

使用道具 举报 送花

  • 打卡等级:偶尔看看III
  • 打卡总天数:42
  • 最近打卡:2025-12-17 10:18:24
已绑定手机

14

主题

93

回帖

822

积分

版主

积分
822
发表于 前天 10:37 | 显示全部楼层
要 做到 USB不停电下载;
要 尝试 AiCube 图形化自动配置生成程序工具;
推荐优先看的:  
printf_usb("Hello World !\r\n")及
USB不停电下载, 演示视频链接:
https://www.stcaimcu.com/thread-19077-1-1.html

下载 最新的 AiCube-ISP-V6.96I 或以上版本软件 !
深圳国芯人工智能有限公司-工具软件

下载 最新的 USB库函数,永远用最新的 USB库函数 !
深圳国芯人工智能有限公司-库函数
下载 最新的 用户手册 !
下载 最新的 上机实践指导书 !

下载 最新的 Ai8051U 用户手册
https://www.stcaimcu.com/data/download/Datasheet/AI8051U.pdf

下载 最新的 Ai8051U 实验指导书,
有 AiCube 图形化自动配置生成程序工具使用说明
https://www.stcaimcu.com/data/do ... %AF%BC%E4%B9%A6.pdf

推荐优先看的 printf_usb("Hello World !\r\n")及usb不停电下载, 演示视频链接

上面是 小李 演示:Ai8051U, printf_usb("Hello World !\r\n")及usb不停电下载@AiCube之图形化程序自动生成

上面是 小赵 演示:Ai8051U, printf_usb("Hello World !\r\n")及usb不停电下载@AiCube之图形化程序自动生成
回复

使用道具 举报 送花

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|深圳国芯人工智能有限公司 ( 粤ICP备2022108929号-2 )

GMT+8, 2025-12-17 13:01 , Processed in 0.101927 second(s), 47 queries .

Powered by Discuz! X3.5

© 2001-2025 Discuz! Team.

快速回复 返回顶部 返回列表