找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 24167|回复: 32
收起左侧

51单片机示波器制作(12864显示带字库)

  [复制链接]
ID:209954 发表于 2017-6-29 18:11 | 显示全部楼层 |阅读模式
  首先说明:我才接触51单片机3个星期(6月8号才高考完),对C语言还很陌生。我就是一个萌新,还希望大佬们多多指导。
  12864(带字库st7920驱动)为显示器,XPT2046为AD转换芯片。(不要问我为什么用这个芯片,开发板自带的坑货。我在网上下载的示波器程序大多以ADC0832或ADC0808为AD转换芯片,结果我都用不了,所以才被迫自写程序。)
  示波器程序由main.c XPT2046.c XPT2046.h三个子文件构成,main.c是我根据网上的一个12864画图程序改编而成我也加了一些注释,后面两个文件取自开发板自带的例程并进行了修改。所以说这个程序基本上不是我编写的,也有很多问题和不足之处还请指正。
下面是效果图。
首先是方波。
psb (2).jpg

psb (3).jpg

psb (4).jpg

psb.jpg


再是正弦波。
由于没有函数发生器,就只能通过手机播放只做好的正弦音频,再检测手机耳机接口信号。
psb (5).jpg

psb (6).jpg

不知道为什么,正弦波有一半不见了,似乎是由于不能检测到负电压还是什么的。。。
下面贴出main.c的代码。
  1. #include <reg52.h>
  2. #include"XPT2046.h"
  3. #define uint unsigned int
  4. #define uchar unsigned char

  5. sbit RS=P2^6; //LCD数据或命令选择端
  6. sbit RW=P2^5; //LCD写入或读出选择端
  7. sbit RST=P1^0; //LCD复位端口
  8. sbit LCDE=P2^7; // LCD使能端
  9. sbit PSB=P3^2;//串行或并行选择端

  10. void delay(uchar i)
  11. {
  12.         while(i--);        
  13. } //延时函数,i=1时延时约10微秒(12M晶振)

  14. void SPI_Start()
  15. {
  16.         CLK = 0;
  17.         CS  = 1;
  18.         DIN = 1;
  19.         CLK = 1;
  20.         CS  = 0;               
  21. } //ADC芯片(XPT2046)初始化函数

  22. void lcd_busy()

  23. {

  24.     RS=0;

  25.     RW=1;

  26.     P0=0XFF;

  27.     LCDE=1;

  28.     delay(14);

  29.     while((P0&0x80)==0x80);
  30.           LCDE=0;

  31. }//LCD12864(st7920驱动)查忙函数

  32. void write_com(uchar com)

  33. {

  34.     lcd_busy();RS=0;

  35.     RW=0;

  36.     LCDE=0;

  37.     P0=com;

  38.     delay(9);

  39.     LCDE=1;

  40.     delay(9);

  41.     LCDE=0;

  42. }//LCD写指令函数

  43. void write_num(uchar num)

  44. {

  45.     lcd_busy();

  46.     RS=1;

  47.     RW=0;

  48.     LCDE=0;

  49.     P0=num;

  50.     delay(7);

  51.     LCDE=1;

  52.     delay(9);

  53.     LCDE=0;

  54. }//LCD写数据函数

  55. uchar read_data()

  56. {

  57.     uchar read;

  58.     lcd_busy();

  59.     RS=1;

  60.     RW=1;

  61.     LCDE=0;

  62.     delay(7);

  63.     LCDE=1;

  64.     delay(9);

  65.     read=P0;

  66.     LCDE=0;

  67.     delay(11);;

  68.    

  69.     return read;

  70. } //LCD读数据函数

  71. void clear_lcd()

  72. {   

  73.     uchar i,j;

  74.     write_com(0x34);

  75.     for(i=0;i<32;i++)             //因为LCD有纵坐标32格所以写三十二次

  76.     {

  77.         write_com(0x80+i);         //先写入纵坐标Y的值

  78.         write_com(0x80);         //再写入横坐标X的值

  79.         for(j=0;j<32;j++)         //横坐标有16位,每位写入两个字节的的数据,也就写入32次

  80.         {                         //因为当写入两个字节之后横坐标会自动加1,所以就不用再次写入地址了。

  81.             write_num(0x00);   

  82.         }

  83.     }

  84.     write_com(0x36);

  85.     write_com(0x30);

  86. } //LCD清屏函数


  87. void put_point(uchar x,uchar y)

  88. {

  89.     uint bt=0,read=0;

  90.     uchar x_adr,y_adr,h_bit,l_bit;

  91.     y_adr=0x80+y%32;            //计算Y轴的地址,应为纵坐标有64个,所有对32求余,当Y大于31时,Y的坐标是下半屏的。

  92.     if(y>31) //计算X轴的地址当Y大于31时X的地址在下半屏,从0X88开始,小于31时X的地址是在上半屏,从0X80开始

  93.         x_adr=0x88+x/16;        

  94.     else

  95.         x_adr=0x80+x/16;

  96.     bt=0x8000>>(x%16); //求这个点到底是在哪个点

  97.     write_com(0x34);

  98.     write_com(0x34);

  99.     write_com(y_adr);     //读取数据的时候要先写入所取数据的地址

  100.     write_com(x_adr);

  101.     read_data();         //读取的第一个字节不要,

  102.     read=read_data();     //从第二个字节开始接收。

  103.     read<<=8;

  104.     read|=read_data();

  105.     bt=bt|read;

  106.     h_bit=bt>>8;

  107.     l_bit=bt;

  108.     write_com(y_adr);     //写入点的时候,重新写入地址,因为地址已经改变。

  109.     write_com(x_adr);

  110.     write_num(h_bit);

  111.     write_num(l_bit);

  112.     write_com(0x36); //开显示

  113.     write_com(0x30);     //转回基本指令集        

  114. }//LCD画点函数

  115. /*

  116. 液晶初始化

  117. */

  118. void lcd_init()

  119. {

  120.     PSB=1;

  121.     RST=1;

  122.     write_com(0x30);     //基本指令操作

  123.     write_com(0x0c);     //开显示

  124.     write_com(0x01);     //清除LCD显示

  125. }

  126. void main()

  127. {   
  128.         
  129.     float i;

  130.     uchar x,n,y;

  131.     lcd_init();

  132.     clear_lcd();

  133.     while(1)

  134.     {     
  135.                 SPI_Start();

  136.         write_com(0x34);

  137.         write_com(0x34);

  138.         write_com(0x80);

  139.         write_com(0x88);

  140.         for(i=16;i>0;i--) //画出X轴

  141.         {

  142.             write_num(0xff);   

  143.         }

  144.         for(i=0;i<64;i++) //画出Y轴

  145.         {

  146.             put_point(0,i);

  147.         }

  148.         for(i=0;i<128;i++)

  149.         {

  150.             x=i;//使横坐标自动向前移

  151.                         n = Read_AD_Data(0xE4);//接收返回的AD值

  152.                         y=32-(n/10);//确定点的纵坐标(纵坐标是AD值,不是电压。)

  153.             put_point(x,y);

  154.                         delay(100);

  155.         }

  156.         write_com(0x36);

  157.         write_com(0x30);

  158.         while(1);

  159.     }

  160.    

  161. }//主函数




复制代码

全部资料下载地址:
示波器(自制).zip (38.06 KB, 下载次数: 237)

评分

参与人数 2黑币 +22 收起 理由
tieq1952 + 10 很给力!
练氏 + 12

查看全部评分

回复

使用道具 举报

ID:1 发表于 2017-6-29 18:16 | 显示全部楼层
好资料,51黑有你更精彩!!!
回复

使用道具 举报

ID:215119 发表于 2017-6-29 20:04 | 显示全部楼层
这个是可以测频率的吗
回复

使用道具 举报

ID:209954 发表于 2017-6-29 21:16 | 显示全部楼层
blink 发表于 2017-6-29 20:04
这个是可以测频率的吗

不可以,就只能显示波形。不过我参考别人写的程序后觉得加装LM393再修改一下程序也许就能测频率。我现在没有LM393,也许你可以试一试。
回复

使用道具 举报

ID:209954 发表于 2017-6-30 09:28 | 显示全部楼层
补充一下:这个程序在测量时需要在信号源内加入一定的直流偏置,否则有部分波形无法正常显示。(就像开始测正弦波时只显示了波形的一半。) IMG_20170630_091823.jpg

回复

使用道具 举报

ID:209954 发表于 2017-7-4 11:17 | 显示全部楼层
这几天我又对程序进行了修改——将AD检测过程与屏幕刷新过程分离,大大提高了检测速度。(此外我将程序修改为5秒自动刷新,以方便观察。)
修改后的main.c如下。
#include <reg52.h>
#include"XPT2046.h"
#define uint unsigned int
#define uchar unsigned char
uchar xdata a[128];

sbit RS=P2^6; //这个是LCD的数据命令选择端
sbit RW=P2^5; //这个是LCD的写入或是读出选择端
sbit RST=P1^0; //这个是LCD的复位端口
sbit LCDE=P2^7; // 这个是LCD的使能端
sbit PSB=P3^2;

void delay(uchar i)
{
        while(i--);       
}

void delayms(uint c)   //误差 0us
{
    uchar a,b;
        for (; c>0; c--)
        {
                 for (b=199;b>0;b--)
                 {
                          for(a=1;a>0;a--);
                 }      
        }
           
}


void lcd_busy()

{

    RS=0;

    RW=1;

    P0=0XFF;

    LCDE=1;

    delay(14);

    while((P0&0x80)==0x80);
          LCDE=0;

}

void write_com(uchar com)

{

    lcd_busy();RS=0;

    RW=0;

    LCDE=0;

    P0=com;

    delay(9);

    LCDE=1;

    delay(9);

    LCDE=0;

}

void write_num(uchar num)

{

    lcd_busy();

    RS=1;

    RW=0;

    LCDE=0;

    P0=num;

    delay(7);

    LCDE=1;

    delay(9);

    LCDE=0;

}

uchar read_data()

{

    uchar read;

    lcd_busy();

    RS=1;

    RW=1;

    LCDE=0;

    delay(7);

    LCDE=1;

    delay(9);

    read=P0;

    LCDE=0;

    delay(11);;



    return read;

}

void clear_lcd()

{   

    uchar i,j;

    write_com(0x34);

    for(i=0;i<32;i++)             //因为LCD有纵坐标32格所以写三十二次

    {

        write_com(0x80+i);         //先写入纵坐标Y的值

        write_com(0x80);         //再写入横坐标X的值

        for(j=0;j<32;j++)         //横坐标有16位,每位写入两个字节的的数据,也就写入32次

        {                         //因为当写入两个字节之后横坐标会自动加1,所以就不用再次写入地址了。

            write_num(0x00);   

        }

    }

    write_com(0x36);

    write_com(0x30);

}

void put_point(uchar x,uchar y)

{

    uint bt=0,read=0;

    uchar x_adr,y_adr,h_bit,l_bit;

    y_adr=0x80+y%32;            //计算Y轴的地址,应为纵坐标有64个,所有对32求余,当Y大于31时,Y的坐标是下半屏的。

    if(y>31) //计算X轴的地址当Y大于31时X的地址在下半屏,从0X88开始,小于31时X的地址是在上半屏,从0X80开始

        x_adr=0x88+x/16;        

    else

        x_adr=0x80+x/16;

    bt=0x8000>>(x%16); //求这个点到底是在哪个点

    write_com(0x34);

    write_com(0x34);

    write_com(y_adr);     //读取数据的时候要先写入所取数据的地址

    write_com(x_adr);

    read_data();         //读取的第一个字节不要,

    read=read_data();     //从第二个字节开始接收。

    read<<=8;

    read|=read_data();

    bt=bt|read;

    h_bit=bt>>8;

    l_bit=bt;

    write_com(y_adr);     //写入点的时候,重新写入地址,因为地址已经改变。

    write_com(x_adr);

    write_num(h_bit);

    write_num(l_bit);

    write_com(0x36); //开显示

    write_com(0x30);     //转回基本指令集        

}

/*

液晶初始化

*/

void lcd_init()

{

    PSB=1;

    RST=1;

    write_com(0x30);     //基本指令操作

    write_com(0x0c);     //开显示

    write_com(0x01);     //清除LCD显示

}

void main()

{   
        while(1)

        {

    uchar x,i,y;

    lcd_init();

    clear_lcd();

        write_com(0x34);

        write_com(0x34);

        write_com(0x80);

        write_com(0x88);

        for(i=16;i>0;i--) //画出X轴

        {

            write_num(0xff);   

        }

        for(i=0;i<64;i++) //画出Y轴

        {

            put_point(0,i);

        }

                for(i=0;i<128;i++)
                {
                    a[i] = Read_AD_Data(0xE4);
                }

        for(i=0;i<128;i++)

        {

            x=i;

                        y=32-(a[i]/10);

            put_point(x,y);

        }

        write_com(0x36);

        write_com(0x30);

                delayms(5000);
    }



}





回复

使用道具 举报

ID:217359 发表于 2017-7-5 14:45 | 显示全部楼层
6666
厉害了
回复

使用道具 举报

ID:220115 发表于 2017-7-16 21:08 | 显示全部楼层
15576118519 发表于 2017-7-4 11:17
这几天我又对程序进行了修改——将AD检测过程与屏幕刷新过程分离,大大提高了检测速度。(此外我将程序修改 ...

a=Read_AD_Data(0xE4);这句代码怎么来的?
回复

使用道具 举报

ID:98792 发表于 2017-7-27 14:49 | 显示全部楼层
XPT2046是AD转换芯片?你确定?不是触摸屏控制芯片?楼主你逗我呢
回复

使用道具 举报

ID:48413 发表于 2017-8-7 19:28 | 显示全部楼层
谢谢楼主的分享
回复

使用道具 举报

ID:220927 发表于 2017-12-6 14:08 | 显示全部楼层
负半周期电压不见了是楼主对电位差没有理解,如果理解了这样就能采集到负半周期的波形了,频率这种定西还要启动定时器,真麻烦
回复

使用道具 举报

ID:253767 发表于 2017-12-21 15:20 | 显示全部楼层
这是个很实用的家伙
回复

使用道具 举报

ID:263148 发表于 2017-12-26 15:56 | 显示全部楼层
谢谢楼主的分享
回复

使用道具 举报

ID:302712 发表于 2018-4-19 10:00 | 显示全部楼层
学习中,谢谢分享
回复

使用道具 举报

ID:319015 发表于 2018-4-29 17:11 | 显示全部楼层
积分不够下,能透漏下XPT2046.c和 XPT2046.h的内容嘛?谢
回复

使用道具 举报

ID:8130 发表于 2018-6-8 21:15 | 显示全部楼层
19960601 发表于 2017-7-27 14:49
XPT2046是AD转换芯片?你确定?不是触摸屏控制芯片?楼主你逗我呢

别逗,XPT2046是一种典型的逐次逼近型模数转换器(SARADC),自己百度
回复

使用道具 举报

ID:384206 发表于 2018-10-12 15:14 | 显示全部楼层
你好  我在用普中科技板子上的xpt2046写示波器的程序   但是 我是的外部输入端(12脚)  在没有输入的时候 数码管也会显示900=1500 的示数 ,  并且在我接上正弦信号后没有任何反应, 12脚接地也没反应 这是为什么 ...  求解
回复

使用道具 举报

ID:399179 发表于 2018-10-12 19:29 来自手机 | 显示全部楼层
这个能不能用?
回复

使用道具 举报

ID:229939 发表于 2018-10-20 10:46 | 显示全部楼层
学习中,谢谢分享
回复

使用道具 举报

ID:511507 发表于 2019-4-14 20:04 来自手机 | 显示全部楼层
这个read_data函数是干嘛用的
回复

使用道具 举报

ID:481742 发表于 2019-5-3 11:30 | 显示全部楼层
看看
回复

使用道具 举报

ID:481742 发表于 2019-5-3 11:31 | 显示全部楼层
试试能不能用
回复

使用道具 举报

ID:544490 发表于 2019-5-25 09:36 | 显示全部楼层
用你这个程序有的点偏离比较大是什么原因
回复

使用道具 举报

ID:118132 发表于 2020-3-17 17:14 | 显示全部楼层
谢谢分享,领教了
回复

使用道具 举报

ID:702057 发表于 2020-3-17 18:22 | 显示全部楼层
跪拜,大神啊!
回复

使用道具 举报

ID:288930 发表于 2020-3-19 15:01 | 显示全部楼层
很好,谢谢分享!
回复

使用道具 举报

ID:696556 发表于 2020-3-25 23:51 | 显示全部楼层
接线图分享一下呗
回复

使用道具 举报

ID:253767 发表于 2020-5-26 07:38 | 显示全部楼层
谢谢分享!!!
回复

使用道具 举报

ID:866313 发表于 2021-1-15 20:27 | 显示全部楼层
山椒鸡爪 发表于 2017-7-16 21:08
**** 作者被禁止或删除 内容自动屏蔽 ****

XPT2046的命令,0xE4选择的是AUX输入模拟信号
回复

使用道具 举报

ID:102800 发表于 2021-1-21 09:39 | 显示全部楼层
谢谢分享最近正在搞这个东东
回复

使用道具 举报

ID:876818 发表于 2021-1-22 08:56 | 显示全部楼层
好资料,51黑有你更精彩!!!
回复

使用道具 举报

ID:689120 发表于 2021-1-23 09:59 | 显示全部楼层
谢谢楼主,有电路图就更好了。
回复

使用道具 举报

ID:880853 发表于 2021-1-27 11:30 | 显示全部楼层
厉害了,一直都打算玩单片机,都不敢动手,看了你们的帖子有点跃跃欲试了
回复

使用道具 举报

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

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

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