打印
[PIC®/AVR®/dsPIC®产品]

877A单片机用MPLAB X IDE写一个LCD12864的程序出现异常

[复制链接]
1380|20
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tree844|  楼主 | 2021-12-6 23:31 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 pzsh 于 2021-12-24 11:20 编辑

单片机型号是PIC16F877A。
我在写一个LCD12864的程序,问题出在忙检测函数。并行数据的读写通过PORTD口。
一开始我的程序是这样的:
void BusyCheck(void)
{
    unsigned char temp;
    TRISD = 0xFF;       //数据口设为输入
    RS = 0;
    RW = 1;
    while(1)
    {
        E = 1;
        temp = PORTD;   //读取DB7
        E = 0;
        if((temp & 0x80) == 0)
        {
            TRISD = 0x00;
            break;
        }            
    }   
}
但是一直跳不出while循环。
之后我添加了一个__bit类型的变量busy,就能正常运行了,函数改为如下:
void BusyCheck(void)
{
    busy = 1;
    unsigned char temp;
    TRISD = 0xFF;       //数据口设为输入
    RS = 0;
    RW = 1;
    while(busy)
    {
        E = 1;
        temp = PORTD;   //读取DB7
        E = 0;
        if((temp & 0x80) == 0)
        {
            TRISD = 0x00;
            busy = 0;            
        }            
    }   
}
甚至我还做了如下调试:根据第二种程序,别的什么都不该,就是把while语句的判断条件改成while(1),程序就不能正常执行。
void BusyCheck(void)
{
    busy = 1;
    unsigned char temp;
    TRISD = 0xFF;       //数据口设为输入
    RS = 0;
    RW = 1;
    while(1)//仅仅修改此处,把while(busy)改成while(1)
    {
        E = 1;
        temp = PORTD;   //读取DB7
        E = 0;
        if((temp & 0x80) == 0)
        {
            TRISD = 0x00;
            busy = 0;
            break;
        }            
    }   
}


但是从逻辑上讲,如果busy = 0;能被执行,那么break肯定也能;break能执行又怎么会一直死在while语句里面不能出来呢?之后我进行了单步调试,结果更加令我诧异:
采用第一种程序(即不用busy变量),temp读取的值最高位一直是1,通常是0x81;而改用第二种程序,temp就能读取到0x00。但是我除了这个函数之后没有改变任何其他地方,为什么temp从PORTD口读到的值会不同呢?

使用特权

评论回复
沙发
tree844|  楼主 | 2021-12-6 23:34 | 只看该作者
完整程序如下:
// CONFIG
#pragma config FOSC = XT        // Oscillator Selection bits (XT oscillator)
#pragma config WDTE = OFF       // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = OFF      // Power-up Timer Enable bit (PWRT disabled)
#pragma config BOREN = OFF      // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF        // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = OFF        // Data EEPROM Memory Code Protection bit (Data EEPROM code protection off)
#pragma config WRT = OFF        // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = OFF         // Flash Program Memory Code Protection bit (Code protection off)

#include <xc.h>

#define _XTAL_FREQ 4000000

__bit busy;
#define RS   RA5
#define RW   RA4
#define E    RA3
#define PSB  RA2
#define nRST RA1

void init(void);
void BusyCheck(void);
void WriteCmd(unsigned char cmd);
void WriteData(unsigned char dat);
void SetPos(unsigned char x, unsigned char y);
void Dis_Str(unsigned char *s);
void init_LCD(void);

void main(void)
{
    unsigned char sec = 0;
    init();
   
    init_LCD();
   
    SetPos(0,1);
    Dis_Str("第一行,第二格");
    SetPos(2,2);
    Dis_Str("time:");
    while(1)
    {
        SetPos(2,6);
        __delay_ms(1000);
        sec++;
        if(sec > 60)
            sec = 0;
        WriteData(sec / 10 + '0');
        WriteData(sec % 10 + '0');
    }
}

void init(void)
{
    PORTA = 0x02;
    TRISA = 0x00;
    ADCON1 = 0x06;
   
    PORTD = 0x00;
    TRISD = 0x00;
}

void BusyCheck(void)
{
    busy = 1;
    unsigned char temp;
    TRISD = 0xFF;       //数据口设为输入
    RS = 0;
    RW = 1;
    while(busy)
    {
        E = 1;
        temp = PORTD;   //读取DB7
        E = 0;
        if((temp & 0x80) == 0)
        {
            TRISD = 0x00;
            busy = 0;
            break;                   //照理说这里的brake语句可以跳出循环,但是不行,必须用busy变量的方法,原因不明。
        }            
    }   
}

void WriteCmd(unsigned char cmd)
{
    BusyCheck();
    RS = 0;
    RW = 0;
    E = 1;
    PORTD = cmd;
    E = 0;
}

void WriteData(unsigned char dat)
{
    BusyCheck();
    RS = 1;
    RW = 0;
    E = 1;
    PORTD = dat;
    E = 0;
}

void SetPos(unsigned char x, unsigned char y)
{
    unsigned char pos;
    switch(x)
    {
        case 0:x = 0x80;break;
        case 1:x = 0x90;break;
        case 2:x = 0x88;break;
        case 3:x = 0x98;break;
        default:break;
    }
    pos = x + y;
    WriteCmd(pos);
}

void Dis_Str(unsigned char *s)
{
    while(*s)
        WriteData(*s++);
}

void init_LCD(void)
{
    nRST = 1;   
    PSB = 1;   
    WriteCmd(0x30);
    WriteCmd(0x0C);
    WriteCmd(0x01);
    __delay_ms(1000);
}


使用特权

评论回复
板凳
tree844|  楼主 | 2021-12-7 22:05 | 只看该作者
有大神遇到过这种问题吗?

使用特权

评论回复
地板
tree844|  楼主 | 2021-12-8 21:38 | 只看该作者
时序之类的应该没有问题,硬件可能性也不大。

使用特权

评论回复
5
pzsh| | 2021-12-9 10:00 | 只看该作者
877A比较老了,如果可能的话,用新的料号使用MCC试一下

使用特权

评论回复
6
tree844|  楼主 | 2021-12-9 10:02 | 只看该作者
pzsh 发表于 2021-12-9 10:00
877A比较老了,如果可能的话,用新的料号使用MCC试一下

确实老了,只作学习之用。
关键在于,我看不出程序中的逻辑问题,我觉得以上三种程序应该都能运行才对啊。

使用特权

评论回复
7
lcczg| | 2021-12-9 15:33 | 只看该作者
直接读RD7呐?
if(PORTDbits.RD7 == 0)

使用特权

评论回复
8
tree844|  楼主 | 2021-12-9 19:26 | 只看该作者
lcczg 发表于 2021-12-9 15:33
直接读RD7呐?
if(PORTDbits.RD7 == 0)

根据LCD的datasheet,并不能这样做,必须在 E = 0 之前先用一个变量暂时存储数据。
我的疑惑主要是在:第一、第三种程序在逻辑上是可行的,和第二种程序相比改动也极小,为什么却读取不到LCD的准备完成信号。

使用特权

评论回复
9
tpgf| | 2022-1-4 13:18 | 只看该作者
这个是一个并口屏?

使用特权

评论回复
评论
tree844 2022-1-11 21:20 回复TA
是的。 
10
renzheshengui| | 2022-1-4 13:20 | 只看该作者
是不是晶振的问题啊  能初始化成功吗

使用特权

评论回复
评论
tree844 2022-1-11 21:21 回复TA
不是晶振的问题,我的开发板功能是正常的。 
11
wakayi| | 2022-1-4 13:21 | 只看该作者
io口需要配置成什么模式呢

使用特权

评论回复
评论
tree844 2022-1-11 21:22 回复TA
我在 init() 函数中有配置,您可以看下。 
12
wowu| | 2022-1-4 13:22 | 只看该作者
会不会是干扰信号产生的呢

使用特权

评论回复
评论
tree844 2022-1-11 21:22 回复TA
应该不是,就是改了代码就好了。 
13
xiaoqizi| | 2022-1-4 13:33 | 只看该作者
是不是工具的问题啊

使用特权

评论回复
评论
tree844 2022-1-11 21:24 回复TA
硬件连接和开发板是没有问题的,最大的可能是MPLAB X IDE,或者说XC8编译器,这个工具我在用的时候软件方面遇到一些奇怪的问题,我会单独开个帖子讲。 
14
木木guainv| | 2022-1-4 13:37 | 只看该作者
稍微调整一下延时呢

使用特权

评论回复
评论
tree844 2022-1-11 21:24 回复TA
和延时绝对没有关系。 
15
ar3000a| | 2022-2-10 03:11 | 只看该作者
根据经验,同样型号的LCD,品牌多质量差距大,出问题的几率大。

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

个人签名:莫等闲、白了少年头,空悲切!

13

主题

243

帖子

3

粉丝