proteus仿真stc15--仿真开发板

转载自:
https://blog.csdn.net/weixin_44578655/article/details/105646300

文章目录

        • 简要
        • 2020.4.20:
          • 今日测试: IO口模式配置 、LCD12864 、硬件PWM
        • 2020.5.16
          • 今日测试:定时器1T/12T模式
        • 2020.5.20
          • 今日测试:串口收发数据
        • 2020.5.21
          • 今日测试:矩阵键盘
        • 2020.5.26
          • 今日测试:SPI驱动74HC595
        • 2020.6.3
          • 今日测试:ADC

简要

proteus在8.9版本正式支持stc单片机,虽然只有一个型号(stc15w4k32s4),不过这是stc家功能很齐全的一款的单片机。
最近刚好开单片机课程使用的就是这款,因为疫情上网课没有实物,就用proteus做了一块仿真开发板。
仿真开发板的原型是stc家的一款试验箱:
proteus仿真stc15--仿真开发板_第1张图片

仿真开发板全貌:
proteus仿真stc15--仿真开发板_第2张图片
开发板链接:
链接:https://pan.baidu.com/s/1xeccrGQLA_kM-TxT5n8-fg
提取码:vwip

之所以选这款,是因为stc-isp有它的全部例程,开发板和例程都白嫖,还要啥自行车,当然仿真会有部分BUG,实物才是正道,例程我随缘测试吧,测试效果会后续更新,有想帮忙一起弄的小伙伴欢迎转载。
例程:
proteus仿真stc15--仿真开发板_第3张图片

2020.4.20:

今日测试: IO口模式配置 、LCD12864 、硬件PWM

问题:

  1. IO口模式无法配置,各IO口不管如何配置,都是准双向IO口模式,网上资料较少,没找到解释。
    因为无法将IO口配置为高阻态,所以ADC无法测试。
    后来发现例程对于ADC的初始化中,并没有将IO口配置为高阻态,后文我又重新测试了一下ADC。

  2. proteus自带的库中,没有淘宝常见的那种带中文字库的LCD12864,在网上翻到有大佬做了一个:
    http://www.51hei.com/bbs/dpj-94761-1.html
    折腾了一下午,能显示了,但这个12864有BUG,只能显示汉字,不支持ASCII码,而且显示屏左上角的定位孔必须与原理图原点的位置重合,才能显示正常,若不对齐,效果如下:
    proteus仿真stc15--仿真开发板_第4张图片
    对齐以后效果如下(屏幕左侧的几串数字估计也是BUG):
    proteus仿真stc15--仿真开发板_第5张图片
    硬件PWM正常,效果如下:
    proteus仿真stc15--仿真开发板_第6张图片

2020.5.16

今日测试:定时器1T/12T模式

有同学反应proteus仿真STC15时,时钟不正确。
今天测试了一下,时钟是准确的。
之前还担心,stc15的定时器工作在1T模式下和12T模式下,proteus能否仿真出真实的效果。今天测试了一下,12T模式下仿真的确比1T模式下仿真慢12倍。

先设为12T模式:
proteus仿真stc15--仿真开发板_第7张图片

在12T模式下,程序运行至12s时,引脚输出发生跳变,小灯亮起:
proteus仿真stc15--仿真开发板_第8张图片
配置为1T模式,程序仅改这一处:
proteus仿真stc15--仿真开发板_第9张图片

程序运行至1s,引脚跳变,小灯亮起,比12T模式下快12倍
proteus仿真stc15--仿真开发板_第10张图片

proteus仿真时运行比较慢,左下角的计时才是程序真实的运行时间。

2020.5.20

今日测试:串口收发数据

proteus中提供了一个虚拟串口终端,类似串口助手可以收发数据。
proteus仿真stc15--仿真开发板_第11张图片
51单片机的串口收发有两种方式:中断方式和查询方式。
在仿真中串口发送使用查询方式和中断方式都可以,数据正常。
串口接收时,仅能使用中断方式,查询方式无法正常收到数据,测试时发现RI始终没被置一。

串口相关代码:

void UART_Init(void)
{
	ACC = P_SW1;
    ACC &= ~(S1_S0 | S1_S1);    //S1_S0=0 S1_S1=0
    P_SW1 = ACC;                //(P3.0/RxD, P3.1/TxD)

    SCON = 0x50;                //8位可变波特率

    AUXR = 0x40;                //定时器1为1T模式
    TMOD = 0x00;                //定时器1为模式0(16位自动重载)
    TL1 = (65536 - (FOSC/4/BAUD));   //设置波特率重装值
    TH1 = (65536 - (FOSC/4/BAUD))>>8;
    TR1 = 1;                    //定时器1开始启动
    ES = 1;                     //使能串口中断
    EA = 1;
}
void Uart() interrupt 4 using 1
{
    if (RI)
    {
        RI = 0;                 //
		rx_temp = SBUF;
    }
    if (TI)
    {
        TI = 0;                 //
        busy = 0;               //
    }
}
void SendData(unsigned char dat)
{
    while (busy);               //
    ACC = dat;                  //
    busy = 1;
    SBUF = ACC;                 //
}

2020.5.21

今日测试:矩阵键盘

开发板原来使用的是P0口接矩阵键盘,但在proteus中的STC15的P0口有严重的BUG,详见这篇帖子:
https://blog.csdn.net/weixin_44578655/article/details/106245015
我把P0换成了P6,并且将限流、上拉电阻全部去掉了,总算能凑合用了。
proteus仿真stc15--仿真开发板_第12张图片
我没使用stc-isp里的矩阵键盘例程(没看太懂),自己按习惯写了一个:

//矩阵键盘扫描函数
//有按键按下时返回字符:'0'~'F',无按键按下时返回0
uchar IO_KeyScan(void)
{
	uchar X_temp = 0,Y_temp = 0;
	uchar Key_res = 0;
	static Key_down = 0;
	
	P6 = 0XFF;	//未知BUG,必须加这句,否则下次读到的数据是错的
	P6 = 0XF0;	//高4位置1,低4位置0,此时有按键按下时,高四位的某一位会被拉低,由此定位按下的按键在第几行
	X_temp = P6 ^ 0XF0;
	if(X_temp )	//如果检测到某行有按键按下(有按键按下时,高四位会有一位被拉低)
	{
		if(Key_down == 0)	//等待按键松开,防止重入
		{
			switch(X_temp)
			{
				case 0x80:
					P6 = 0XFF;		//未知BUG,必须加这句,否则下次读到的数据是错的
					P6 = 0X0F;		//低4位置1
					Y_temp = P6 ^ 0X0F;
					switch(Y_temp)
					{
						case 0x08: Key_res = 'F'; break;
						case 0x04: Key_res = 'E'; break;
						case 0x02: Key_res = 'D'; break;
						case 0x01: Key_res = 'C'; break;
						default: break;
					}
					break;
				case 0x40:
					P6 = 0XFF;
					P6 = 0X0F;
					Y_temp = P6 ^ 0X0F;
					switch(Y_temp)
					{
						case 0x08: Key_res = 'B'; break;
						case 0x04: Key_res = 'A'; break;
						case 0x02: Key_res = '9'; break;
						case 0x01: Key_res = '8'; break;
						default: break;
					}
					break;
				case 0x20:
					P6 = 0XFF;
					P6 = 0X0F;
					Y_temp = P6 ^ 0X0F;
					switch(Y_temp)
					{
						case 0x08: Key_res = '7'; break;
						case 0x04: Key_res = '6'; break;
						case 0x02: Key_res = '5'; break;
						case 0x01: Key_res = '4'; break;
						default: break;
					}
					break;
				case 0x10:
					P6 = 0XFF;
					P6 = 0X0F;
					Y_temp = P6 ^ 0X0F;
					switch(Y_temp)
					{
						case 0x08: Key_res = '3'; break;
						case 0x04: Key_res = '2'; break;
						case 0x02: Key_res = '1'; break;
						case 0x01: Key_res = '0'; break;
						default: break;
					}
					break;
				default:
					break;
			}
		}
	}
	else
		Key_down = 0;	//按键被松开
	
	if(Key_res)
		Key_down = 1;	//标志按键被按下,防止重入

	return Key_res;
}

测试结果:
依次按下16个按键,串口输出的数据’0’~‘F’
proteus仿真stc15--仿真开发板_第13张图片

2020.5.26

今日测试:SPI驱动74HC595

74HC595是一颗串行输入并行输出的IC,通常用来驱动数码管、led点阵等,可以节省很多IO口.
74HC595的时序跟SPI很像,所以可以用硬件SPI驱动74HC595
在开发板上,74HC595用来驱动数码管,连接的就是SPI1的引脚。

跟串口类似,SPI发送数据同样有两种方式:查询方式和中断方式,这两种方式在仿真中均正常。

由于spi1的引脚有好几组,开发板上使用的是P4.3、P4.0、P5.4这一组引脚,选择哪一组引脚需要通过程序配置。
stc-isp中与SPI相关的例程有好几个,它们配置SPI引脚的代码不一样(不知道为什么),主要有两种:
第一种:配置AUXR1
proteus仿真stc15--仿真开发板_第14张图片
第二种:配置ACC和PSW
proteus仿真stc15--仿真开发板_第15张图片
在仿真中,第一种配置spi无效,第二种配置spi正常。

下面是查询方式和中断方式SPI发送数据的相关代码:

查询方式SPI初始化代码:

void spi_init()	//spi初始化
{
	/****官方例程中出现的,去掉这一段后spi失效***/
	ACC = P_SW1;           		//切换到第三组SPI
	ACC &= ~(SPI_S0 | SPI_S1);  //SPI_S0=0 SPI_S1=1
	ACC |= SPI_S1;      		//(P5.4/SS_3, P4.0/MOSI_3, P4.1/MISO_3, P4.3/SCLK_3)
	P_SW1 = ACC;  
	SPSTAT = SPIF | WCOL;       //清除SPI状态
	/********/
	
	SPCTL=(SSIG<<7)+(SPEN<<6)+(DORD<<5)+(MSTR<<4)
	     +(CPOL<<3)+(CPHA<<2)+SPEED_4;
}

查询方式发送函数:

//查询方式发送数据
void SPI_SendByte(unsigned char dat)
{
	SPSTAT=SPIF+WCOL;
	SPDAT=dat;
	while((SPSTAT & SPIF)==0);
	SPSTAT=SPIF+WCOL;
}

中断方式SPI初始化代码:

void spi_init()	//SPI初始化
{
	/****官方例程中出现的,去掉这一段后spi失效***/
	ACC = P_SW1;           		//切换到第三组SPI
	ACC &= ~(SPI_S0 | SPI_S1);  //SPI_S0=0 SPI_S1=1
	ACC |= SPI_S1;      		//(P5.4/SS_3, P4.0/MOSI_3, P4.1/MISO_3, P4.3/SCLK_3)
	P_SW1 = ACC;  
	SPSTAT = SPIF | WCOL;       //清除SPI状态
	/********/
	
	SPCTL=(SSIG<<7)+(SPEN<<6)+(DORD<<5)+(MSTR<<4)
	     +(CPOL<<3)+(CPHA<<2)+SPEED_4;
	IE2 |= ESPI;	//使能SPI传输中断
	EA=1;			//开启中断
}

中断方式发送函数:

//中断方式发送数据
void SPI_SendByte(unsigned char dat)
{
	g_fSpiBusy = TRUE;
	SPDAT=dat;
  	while (g_fSpiBusy);                         //等待SPI数据传输完成
}
//spi中断
void spi_isr() interrupt 9 using 1
{
    SPSTAT = SPIF | WCOL;                       //清除SPI状态位
    g_fSpiBusy = FALSE;
}

数码管扫描函数:

//数码管扫描
void seg7scan(unsigned char index1,unsigned char index2)
{
	SPI_SendByte(~T_COM[index1]);	//发送位选
	SPI_SendByte(t_display[index2]);//发送段选
	HC595_RCLK=1;	//将P5.4置1,更新数据
	HC595_RCLK=0;	//将P5.4置0
}
//消隐
void seg_clear(void)
{
	SPI_SendByte(0x00);	//位选清零
	SPI_SendByte(0x00);	//段选清零
	HC595_RCLK=1;	//将P5.4置1,更新数据
	HC595_RCLK=0;	//将P5.4置0
}

main函数的大循环:

	while(1)
	{
		for(i=0;i<8;i++)
		{
			seg7scan(i,i);	//数码管扫描
			Delay20ms();	//延时20ms
			seg_clear();	//消隐
		}
	}

显示效果:

proteus仿真stc15--仿真开发板_第16张图片元器件太多了,仿真运行非常慢,实际现象应该是0123456,由于扫描缓慢,所以截图的瞬间只显示了34。

单独仿真这部分时,显示正常(因为运行的快一些,看不出来扫描的过程):
proteus仿真stc15--仿真开发板_第17张图片

2020.6.3

今日测试:ADC

之前仿真的时候,stc15的IO口无法配置为高阻态,所以我直接放弃测试ADC了,因为即便能读出来数,这个ADC也无法正常应用(输入阻抗太小)。
最近还是测试了一下:
使用滑动变阻器:
proteus仿真stc15--仿真开发板_第18张图片
proteus仿真stc15--仿真开发板_第19张图片可以看到,ADC引脚的输入阻抗异常,滑动变阻器调到2%(对地20Ω),电压竟然还能到2.5V,输出电流达到125mA。
不管是准双向还是推挽模式,都不可能出现这种情况。
抛开输入阻抗的问题,ADC在对应电压下的读数,还是比较准确的。
0.04V时,读数是02(16进制)
proteus仿真stc15--仿真开发板_第20张图片
0.154V时,读数是08(16进制)
proteus仿真stc15--仿真开发板_第21张图片
1.69V时,读数是56(16进制)
proteus仿真stc15--仿真开发板_第22张图片ADC的相关代码:

/*----------------------------
初始化ADC
----------------------------*/
void InitADC()
{
	P1M0 = 1;						//将P1口配置为高阻态
	P1M1 = 1;		
	P1 = 0Xff;
    P1ASF = 0xff;                   //设置P1口为AD口

    ADC_RES = 0;                    //清除结果寄存器
    ADC_CONTR = ADC_POWER | ADC_SPEEDLL;
    Delay(2);                       //ADC上电并延时
}

/*----------------------------
读取ADC结果
参数:ch,通道0~7
----------------------------*/
BYTE GetADCResult(BYTE ch)
{
    ADC_CONTR = ADC_POWER | ADC_SPEEDLL | ch | ADC_START;
    _nop_();                        //等待4个NOP
    _nop_();
    _nop_();
    _nop_();
    while (!(ADC_CONTR & ADC_FLAG));//等待ADC转换完成
    ADC_CONTR &= ~ADC_FLAG;         //Close ADC

    return ADC_RES;                 //返回ADC结果
}

输出结果时,又发现一点问题,原来是打算把ADC的结果(8位)使用sprintf格式化输出为字符,但这种方式打印出的数据完全错误…

/***下面这样使用sprintf出问题,未知bug***/
sprintf(tx_temp,"ch%d:%x\r\n",ch,GetADCResult(ch));
SendString(tx_temp);	//串口发送字符串
/******/		

现象:
proteus仿真stc15--仿真开发板_第23张图片
只能直接发送16进制数:

/***只发送1字节16进制数,数据正常***/
SendData(GetADCResult(ch));
/******/

注意在串口终端窗口处右键勾选Hex Display Mode

你可能感兴趣的:(proteus,stc15)