浦江一水 发表于 2024-12-5 10:36:37

当传统89C52单片机开发学习板遇到AI8051U(DIP40)时

**当传统STC89C52单片机开发学习板遇到AI8051U(DIP40)时**

!(data/attachment/forum/202412/05/102316u80q0m23xs7b2bdm.jpg "QX-MCS51_V3.0开发板A.jpg")

这是本人在"初试8051U,做个小实验..."一帖中用到的QX-MCS51_V3.0单片机开发学习板。https://www.stcaimcu.com/forum.php?mod=viewthread&tid=9591&extra=page%3D1

平心而论,STC89C52还是一块很不错的芯片,印象中,好像就是它这个系列,开创了51单片机串口下载仿真调试的先河。因此,多年以来,市面上所看到的大多数51单片机开发板,几乎都是基于这块芯片而设计的。
QX-MCS51开发板与其他品牌的51单片机开发板一样,都具备STC89C52本身功能的基本实验条件。
而如今AI8051U的问世,特别是DIP40封装AI8051U的出现,使得STC89C52大有被完全取代的可能。

然而,取代也并非是闭着眼睛、可随意为之的。
尤其是新手初学者,本人在学习实验中,有几点思考和体会,在此聊聊...
当AI8051U(DIP40)插入原来STC89C52位置时:

**1,引脚对比分析**

!(data/attachment/forum/202412/05/102428eyrrwqdqz1y73zwx.jpg "STC8051U_DIP40引脚定义.jpg")

上图对比,显然AI8051U的功能多了许多。除了电源、接地和P0-P3(8*4=32)以外,不同之处有:
第9脚: 复位RST端。
原STC89C52的复位端是高电平复位,而AI8051U的复位端是低电平复位。
因此,开发板上的复位按钮对AI8051U不起复位作用。
在实验时,可通过P47RST=0将此脚作为普通IO用。
第18,19脚:原接外部晶振(12MHz)。
AI8051U无需外接晶振。因此可将原晶振拔出,使得此两脚悬空。
在实验时,可作为P56、P57使用,多了两个IO。
第29、30脚:原用于外部存储器锁存使能控制,一般STC89C52开发板,不访问外部存储器,都会将此脚悬空,直接引到外部排针。

因此,当插入AI8051U实验时,就可利用此两脚,做有关P44、P45的实验。
第31脚:EA,原外部地址总线使能脚,一般STC89C52开发板,不访问外部存储器,都会将此脚上拉至VCC。
因此,当插入AI8051U做实验时,就牺牲了此引脚,不要做有关P46的实验。

**2,下载调试接口**
原STC89C52开发板是利用P30、P31串口作为下载和仿真调试口的。
众多STC89C52开发板也增添了电路,考虑了自动断电上电功能,QX-MCS51开发板也不例外,并在这方面做得比较好。
在做STC89C52实验下载程序时,自动断电上电一直比较顺畅。
因为AI8051U支持串口下载仿真调试,所以不需要做任何改动,可直接用串口下载调试,没有问题。
(AI8051U还支持USB下载调试,笔者已经实验发现,有些开发板,只要做细小改动,也可以实现一根USB线,直接下载调试,将另文描述)

**3,主频时钟选择**
原STC89C52时利用外部晶振(一般是11.0592MHz或12.00MHz),相对决定了主频。
而AI8051U使用内部晶振,主频可选范围较宽,使用上更为灵活多变,明显展示了AI8051U的优势。

**4,编程及初始化IO**
STC89C52上电时,各IO口默认为准双向模式。
而AI8051U不同,上电时除了P30、P31串口特殊用途之外,其它IO都默认为高阻输入,
因此,在做AI8051U实验时,必须首先对IO模式进行正确初始化。

**5,指令模式**
AI8051U的CPU支持8bit和32bit指令模式。
当选用8bit指令模式时, 要引用 8bit指令模式的AI8051U.H头文件,使用C51编译器。
当选用32bit指令模式时,要引用32bit指令模式的AI8051U.H头文件,使用C251编译器。
不必试图去做:
将8bit指令模式的程序代码选用C251编译、运行。
或者试图去做:
将32bit指令模式的程序代码选用C51编译、运行。
因为,这毫无意义。

**6,软件延时调整**
STC89C52可选6T或12T指令周期模式。通常是12T模式。
AI8051U 可选1T或12T指令周期模式。通常是 1T模式。
因此,在相同主频的情况下,通常AI8051U的指令周期要快12倍,
更何况,AI8051U的主频可大幅度提高,所以要充分注意:
原STC89C52的程序代码,未必能完全正常运行。
特别是对于显示屏的驱动,以及像对于DS18B20这样的对时序要求比较严格的器件的读写控制,
在用软件延时的地方,要注意适当的调整,延长延时。

**7,注意一下ISP的版本**
并非最新的ISP版本,对传统的开发板支持得最舒服的。要保存好各种版本的ISP软件,选用最适合的。
本人体会,对于QX-MCS51开发学习板,STCAI-ISP(V6.94H)较好。

以上,是点滴体会,供共同单片机爱好的坛友参考。

浦江一水 发表于 2024-12-5 10:39:05


按照惯例,先来一个跑马灯的实验。

(补充一下此开发板的电原理图,便于读者对照程序容易理解)

//=========================================================================
// 文件: Main.C
// 硬件: AI8051U @ QX-MCS51 V3.0 学习板
// 功能: AI8051U_32位编程入门初步 + 跑马灯程序
// 端口: 8LED: P1.0--P1.7 输出低电平驱动点亮
// 备注: 下载时选择时钟 24MHZ
// 编程: 浦晓明(浦江一水) 2024-12-04
//=========================================================================
#include "AI8051U.h"   //包含此头文件后,不需要再包含"reg52.h"头文件
#include <intrins.h>   //包含循环右移函数_crol头文件

#define MAIN_Fosc24000000L//定义主时钟(下载时可设定)

//全局变量(便于调试观察)
unsigned char LED=0xFC;
//========================================================================
// 函数: void delay_ms(u8 ms)
// 描述: 延时函数。
// 参数: ms,要延时的ms数
// 返回: none.
//========================================================================
void delay_ms(unsigned intms)
{ unsigned int i;
do{ i = MAIN_Fosc / 10000;
      while(--i);
    } while(--ms);
}
//==== 主函数 ============================================================
void main(void)
{ unsigned char i;
WTST= 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
//IO端口初始化:准双向口
P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
P1 = LED;                     //初始化亮灯1111100 点亮两个灯
//--主循环-----------------
while(1)
{ for(i = 0; i < 8; i++)
    { LED = _crol_(LED,1);      //循环左移,LED从右向左移动点亮
      P1 = LED;
      delay_ms(100);      
    }
}
}
//==== END ==========================================================




浦江一水 发表于 2024-12-5 20:53:02

感谢“国学芯用”和“”两位版主的鲜花鼓励和捧场,以及楼上朋友的点赞。

第二个实验程序是关于数码管显示的。
作为一个单片机爱好者, 想必控制数码管显示,应该是一项必须掌握的基本技能了。
这块开发板提供的是8位共阴极的数码管,使用了两片74HC573带锁存功能的8位驱动。
因此,本实验程序是基于这个芯片特点而编写的。
实验的内容看似比较简单。
一上来显示“AI8051U_”;
随后显示PI值“P=3.14159”;      (可以思考一下:如何显示这样一串数据?逐个移位取整求余?)
再显示一个直径变量D“d= 1.00 ”;
然后显示计算的面积值"F=0.78540";
最后进入一个模拟RTC的时钟循环显示。
体现了数码管显示字符串和数值特别是浮点数的编程处理。

主程序:

//********************************************************************************
// 名称: 实验8位数码管动态扫描显示
// 基于: QX-MCS51 V3.0 单片机开发学习板
// 实验: AI8051U 取代 STC89C52 实现8位LED数码管字符串显示.
// 编程: 浦晓明(浦江一水) 2024-12-05
//********************************************************************************
#include "AI8051U.H"
#include "AI8051U_SYS.H"
#include "LED8D.H"

sbit we = P2^7;        //位定义数码管位选锁存器接口
sbit du = P2^6;        //位定义数码管段选锁存器接口
// 常数宏定义
#define   PI    3.14159
/************* 全局变量声明 **************/
char S;            //显示字符串缓存
unsigned char hour; //时
unsigned char min;//分
unsigned char sec;//秒
float    d,F;            //直径和面积变量

/*************外部函数声明和外部变量声明 *****************/
//========================================================================

// 函数: voiddelay_ms(unsigned int ms)
// 描述: 毫秒级延时函数。
// 参数: ms,要延时的ms数,自动适应主时钟.
//=====================================================================
voiddelay_ms(unsigned int ms)
{ unsigned int i;
do{ i = MAIN_Fosc / 10000;
      while(--i);
    } while(--ms);
}
/********************** 模拟RTC演示函数 ***********************/
void RTC(void)
{
if(++sec >= 60)
{ sec = 0;
    if(++min >= 60)
    { min = 0;
      if(++hour >= 24) hour = 0;
    }
}
sprintf(S,"%02d-%02d-%02d",hour,min,sec);//组织字符串
LED8D_Str(0,S);                //送显示段码缓存//(指定从0位置开始显示...)   
}
/******************** 主函数 **************************/
void main(void)
{
SYS_Init();       //系统初始化
LED8D_Init();   //8数码管和定时器0初始化

d=1.0;             //直径m
F=PI*d*d/4;   //求面积
   
LED8D_Str(0,"AI8051U_");   //字符串常量直接显示
delay_ms(3000);                  //延时一下
sprintf(S,"P=%6.5f",PI);       //浮点数转字符串显示PI值3.14159
LED8D_Str(0,S);                  //送显示段码缓存(指定从0位置开始显示...)   
delay_ms(3000);                  //延时一下
sprintf(S,"D= %5.3f ",d);      //浮点数转字符串显示d直径值
LED8D_Str(0,S);                  //送显示段码缓存   
delay_ms(3000);                  //延时一下
sprintf(S,"F=%6.5f",F);      //浮点数转字符串显示F面积值
LED8D_Str(0,S);                  //送显示段码缓存   
delay_ms(3000);                  //延时一下

hour = 11;    //时间初始化
min= 59;
sec= 58;
RTC();
//循环...整型数转字符串显示...
while(1)
{
    if(SecOK)   //1s时间到
    { SecOK = 0;
      RTC();      //模拟RTC时钟显示
    }   
}
}
/***** END *****************************************/
工程文件:供初学单片机技术的爱好者参考。





浦江一水 发表于 2024-12-8 09:13:52

第三个实验包括三个内容:
1, LCD1602显示屏的驱动控制;
2, 四个独立按键的4键8值按键处理;
3, 串口1通讯实验.
最后综合上述三项,做成串口屏:
开机显示

当按下4个独立键其中之一时,在显示屏显示键值,
同时向上位机发送信息:按下了哪个键,是短按还是长按.

如果上位机发送"P10*AI8051U*"+回车换行,
那么,显示屏就会在第二行的0位置开始显示"*AI8051U*"字符串,如图:


主程序:

/******************************************************************
* 工程: Test AI8051U(DIP40) + LCD1602A
* 基于: QX-MCS51_V3.0(STC89C52)单片机开发学习板
* 实验: LCD1602串口显示屏,8位数据控制,USART1_串口通讯.
* 四键: P30/P31/P32/P33,4键8值.
* 说明: 测试AI8051U在传统51实验板上,不做任何硬件改动的实验...
* 编程: 浦晓明(浦江一水) 2024-12-08 ...
*******************************************************************/
#include <stdio.h>
#include <string.h>
#include "AI8051U.h"
#include "KEY4_IO.H"    //4键盘相关函数
#include "LCD1602.H"    //LCD1602A显示屏相关函数

//波特率计算...      
#define FOSC            11059200UL                      //晶振频率
#define BRT         (256 - FOSC / 115200 / 32)    //按照波特率115200计算


sbit LED0= P1^7;             //运行指示

char RxBuf,k;               //串口接收缓存
unsigned short RxCount=0; //接收计数器
char S;                        //LCD显示缓存
unsigned char X,Y;             //坐标
unsigned char RxOK=0;      //串口通信接收到标志

//毫秒级非精确延时
void delay(unsigned int ms)
{ unsigned int x,y;
for(x = ms; x > 0; x--)
for(y =114; y > 0; y--);
}
//串口初始化:
void UART_init()
{
SCON= 0x50;
TMOD= 0x20; //T1工作模式28位自动重装
TH1 = BRT;    //0xFD;
TL1 = BRT;    //0xFD; //比特率115200
TR1 = 1;       //启动T1定时器
AUXR= 0x40;
SM0 = 0;
SM1 = 1;    //串口工作方式1 10位异步
REN = 1;    //串口允许接收
ES= 1;   //串口中断打开
EA= 1;   //开总中断
}
/*----------------------------
通过串口1发送单字节数据
----------------------------*/
void SendData(unsigned char ch)
{
SBUF = ch;               //写数据到UART数据寄存器
while(TI == 0);
TI = 0;
}
/*----------------------------
通过串口1发送字符串
----------------------------*/
void SendString(char *s)
{
while (*s)                   //检测字符串结束标志
{ SendData(*s++); }   //发送当前字符
}
//--------------------------------------------------------------
// 串口1接收中断服务...
//--------------------------------------------------------------
void UART() interrupt 4
{ unsigned char ch=0;
if(RI)                //检测是否接收完成
{ ch=SBUF;       //接收单字节
    RI = 0;         //清除标志位
    if(ch==0x0A)//结尾标志
    { RxOK=1;      //接收到指令串置接收到标志
      if((RxCount>0)&&(RxBuf==0x0D))
      { RxBuf=0;RxBuf=0; }   //清除0x0D,0x0A
    }
    else if(RxCount<19)RxBuf=ch;
}
}
//--------------------------------------------------------------
// 主程序入口
//--------------------------------------------------------------
void main(void)
{ unsigned char i,mode;
unsigned int wait=0;
//IO端口初始化
WTST= 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
//IO端口初始化//全部为准双向口
P0M1 = 0x00;   P0M0 = 0x00;   //设置为准双向口
P1M1 = 0x00;   P1M0 = 0x00;   //设置为准双向口
P2M1 = 0x00;   P2M0 = 0x00;   //设置为准双向口
P3M1 = 0x00;   P3M0 = 0x00;   //设置为准双向口
P4M1 = 0x00;   P4M0 = 0x00;   //设置为准双向口
//外设初始化
UART_init();
LCD_Init();      //初始化LCD1602
LCD_CLS();       //测试清屏函数
LCD_Blink(0);    //测试函数
LCD_Cursor(0);//测试函数
LCD_SetXY(0,0);//测试函数
delay(5);
LED0 = 0; delay(1000); //测试IO口控制 LED0亮
LED0 = 1; delay(1000); //测试IO口控制 LED0灭
//显示屏初始界面
LCD_STR(0,0,"AI8051U_@115200_");
LCD_STR(1,0,"LCD1602__4KEY=00");
//测试通讯口发送字符串
SendString("AI8051U_UART1_OK\r\n");
delay(1000);
//主循环
while(1)
{ k=0;
    k=RDKEY();//扫描按键
    if(k!=0)
    { sprintf(S,"KEY=%02X ",(unsigned int)k);   //当有键按下时, 显示键值,(<1秒为短按 >=1秒为长按)
      LCD_STR(1,10,S);                                 //比如短按Key1,显示=10, 长按显示=11
      P1 = ~k;   //显示键值于LED灯
      switch(k)
      { case 0x10:SendString("短按...\r\n");break;
      case 0x11:SendString("长按...\r\n");break;
      case 0x20:SendString("短按...\r\n");break;
      case 0x21:SendString("长按...\r\n");break;
      case 0x30:SendString("短按...\r\n");break;
      case 0x31:SendString("长按...\r\n");break;
      case 0x40:SendString("短按...\r\n");break;
      case 0x41:SendString("长按...\r\n");break;
      }      
    }
    //运行指示灯...
    if(wait==0){LED0 = 0; }                               // LED亮
    if(wait==200){LED0 = 1; RxOK=0;RxCount=0;RxBuf=0; }// LED灭
    wait=wait<2250?wait+1:0;//计数器循环
    //串口屏通讯处理....
    if(RxOK)//接收到指令串
    { //解析串口接收指令字符串,显示之
      switch(RxBuf)
      { case 'P'://显示指令
                   X=RxBuf-0x30;
                   Y=RxBuf-0x30; if(Y>9)Y=Y-7;
                   for(i=0;i<RxCount-3;i++){ S=RxBuf; S=0; }
                   LCD_STR(X,Y,S);//显示接收字符串
                   break;
      case '@':X=RxBuf-0x30;
                   Y=RxBuf-0x30; if(Y>9)Y=Y-7;
                   LCD_SetXY(X,Y);//光标定位
                   break;
      case '_':mode=RxBuf-0x30;if(mode>0)mode=1;
                   LCD_Cursor(mode);//光标显示      
                   break;
      case '*':mode=RxBuf-0x30;if(mode>0)mode=1;
                   LCD_Blink(mode); //光标闪动
                   break;
      case 'C':mode=RxBuf-0x30;
                   if(mode==0)LCD_STR(0,0,"                ");//清屏第一行
                   if(mode==1)LCD_STR(1,0,"                ");//清屏第二行
                                                                         if(mode>=2)LCD_CLS();                      //清全屏
                   break;
      default:   RxBuf=0; SendString(RxBuf); SendData(0x0D); SendData(0x0A); break;//默认返回原串...
      }
      for(i=0;i<24;i++)RxBuf=0;RxOK=0; RxCount=0;
    }                            //清除接收缓存
    delay(10);   
}
}
//******************************************************************************
// 附件: LCD1602串口屏通信控制指令协议,115200,8,1,N
//----------------------------------------------------------------------
// 共五条指令, 说明如下...
//----------------------------------------------------------------------
// 1, 格式:PXYsss...\r\n   +0D+0A
//    其中:P -- 命令字 指定坐标显示字符串;
//         X -- 显示行坐标, X=0或1,表示第一行或第二行
//         Y -- 显示列坐标, Y=0...F(超过9用ABCDEF的16进制表示)
//         sss... 需要显示的字符串 (长度限于16字符)(LCD1602每行最多显示16字符)   
//         \r\n发送串结尾标志,回车换行符 即+0x0D+0x0A,(以下同)
//    实例:P00ABCDEFG+0.1234(\r\n) 在显示屏第一行首显示"ABCDEFG+0.1234"
//----------------------------------------------------------------------
// 2, 格式:Cm\r\n
//    其中:C -- 命令字 清屏
//         m -- 参数 0:清第一行 1:清第二行 2:清全屏         
//    实例:C0\r\n 显示屏第一行被清空;
//----------------------------------------------------------------------
// 3, 格式:@XY\r\n
//    其中:@ -- 命令字 指定当前光标坐标
//            X,Y -- 参数 X:行 Y:列         
//    实例:@01\r\n 当前光标在第一行第1列 (光标是否显示取决于以下指令)
//----------------------------------------------------------------------
// 4, 格式:*m\r\n
//    其中:* -- 命令字 光标闪烁显示
//             m -- 参数 0:不闪烁 1:闪烁         
//    实例:*1 在先前指定的坐标位置闪烁光标;
//----------------------------------------------------------------------
// 5, 格式:_m\r\n
//    其中:_ -- 命令字 底画线显示
//             m -- 参数 0:不显示底画线 1:显示底画线         
//    实例:_1 在先前指定的坐标位置显示底画线; (底画线显示后,光标闪烁取消)
//*******************************************************************************


工程文件, 供初学单片机爱好者参考.


浦江一水 发表于 2024-12-9 08:56:09

第四个实验是关于8x8点阵LED模块的滚动显示.
QX-MCS51开发板套件中配置了一块8x8点阵LED模块. 这种模块可以接连,扩大显示面积.
在现实生活中,点阵LED显示屏,滚动显示字符串较为常见.
本实验在一块点阵显示屏上滚动循环显示96个ASCII码字符. 实践了单片机控制点阵LED模块的基本技术.




主程序:
//==============================================================================================
// 文件: Main.C
// 硬件: AI8051U @ QX-MCS51 V3.0 学习板
// 功能: AI8051U_32位编程入门初步
// 端口: LED8x8点阵显示模块: P3.4-DIN/P3.5-CLK/P3.6-LAT
// 备注: 下载时选择时钟 24MHZ
// 编程: 浦晓明(浦江一水) 2024-12-08
//==============================================================================================
#include "AI8051U.H"
#include "AI8051U_SYS.H"
#include "ASC8.H"

//点阵模块接口定义
sbit LED8x8_LAT = P3^6;//储存寄存器时钟
sbit LED8x8_CLK = P3^5;//移位寄存器时钟输入端
sbit LED8x8_DIN = P3^4;//串行数据输入端
// --列选通控制-- //
unsigned char code TAB= { 0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F, };
//全局变量
unsigned char ZF;         //字符点阵18字节(显示缓存)
unsigned int m=0,n,i,l=0,p;

//发送一个字节数据给点阵模块
void SendByte( unsigned char dat)
{ unsigned char i;      //循环次数变量
LED8x8_CLK = 0;_nop_(); //拉低移位时钟
LED8x8_LAT = 0;_nop_(); //拉低储存时钟
for( i = 0 ; i < 8 ; i++ ) //循环8次写入一个字节数据
{ if( dat&0x80 ){LED8x8_DIN = 1; }
    else          {LED8x8_DIN = 0; }
    LED8x8_CLK = 1;       //上升沿发送数据
    LED8x8_CLK = 0;
    dat <<= 1;      
}   
}
//主函数入口
void main()
{
SYS_Init();
m=0; l=0; n=0;
//初始化显示缓存
for(n=0;n<3;n++)         //装入三个字符
for(i=0;i<6;i++)         //每字六个字节
{ ZF=ASC8; m++; }
//大循环...
while(1)   
{ for(p=0;p<8;p++){
    for(i=0;i<250;i++) {
    for(m=0;m<8;m++)
    {
      SendByte(TAB);             //列选择                                                                                                                              
      SendByte(ZF);             //行数据(点阵模)
      LED8x8_LAT = 1;_nop_(); //锁存数据
      LED8x8_LAT = 0;_nop_();
    }                } }
    for(i=0;i<18;i++)ZF=ZF;//18字节左移一位
    l++; NOP10();                   //左移一列         
    if(l>5)
    { l=0;
      for(m=12;m<18;m++){ ZF = ASC8; }//加载字符点阵,
      n=n<95?n+1:0;
    } //n=字符编号
}
}


工程文件, 供初学单片机技术的爱好者参考.


zhaoye818 发表于 2024-12-9 09:57:52

<p>必须点赞</p>

浦江一水 发表于 2024-12-10 21:05:57

第五个实验是关于PWM呼吸灯

AI8051U芯片,几乎每个IO口都具有硬件PWM输出功能,使用非常灵活。
本实验将P1口的8个位配置成PWM输出,以实验呼吸灯的效果。
注意,由AI8051U引脚定义图可以看出:
P1.0为PWM1P,P1.1为PWM1N,互为PWM1通道反相互补;
P1.2为PWM2P,P1.3为PWM2N,互为PWM2通道反相互补;

P1.4为PWM3P,P1.5为PWM3N,互为PWM3通道反相互补;
P1.6为PWM4P,P1.7为PWM4N,互为PWM4通道反相互补;
使用相同占空比输出控制时,交替互为亮灭。


实际效果如视频所示:


主程序

//==============================================================================================
// 文件: Main.C
// 硬件: AI8051U @ 清翔 QX-MCS51 V3.0 学习板
// 功能: AI8051U_32位编程入门初步: 呼吸灯实验
// 端口: P1-8位 LED灯
// 备注: 下载时选择时钟 11.0592MHZ
// 编程: 浦晓明(浦江一水) 2024-12-09
//==============================================================================================
#include "AI8051U.H"
#include "AI8051U_SYS.H"


#define Timer0_Reload   (65536UL -(MAIN_Fosc / 1000))//Timer 0 中断频率, 1000次/秒 1ms中断
#define PWM_PERIOD       1023                        //设置周期值


//全局变量(便于调试观察)
u16 PWM_Duty;   //占空比值
bit PWM_Flag;   //切换标志
//函数说明
void UpdatePwm(void);


//主函数入口
void main()
{
SYS_Init();
//定时器0初始化
AUXR = 0x80;      //定时器0设为1T,16位自动重载
TH0 = (u8)(Timer0_Reload / 256);
TL0 = (u8)(Timer0_Reload % 256);
ET0 = 1;            //定时器0中断使能
TR0 = 1;            //定时器0启动


PWMA_CCER1 = 0x00;//写 CCMRx 前必须先清零 CCxE 关闭通道
PWMA_CCER2 = 0x00;
PWMA_CCMR1 = 0x60;//通道模式配置
PWMA_CCMR2 = 0x60;
PWMA_CCMR3 = 0x60;
PWMA_CCMR4 = 0x60;
PWMA_CCER1 = 0x55;//配置通道输出使能和极性
PWMA_CCER2 = 0x55;


PWMA_CCMR1 |= 0x08; //开启PWMA_CCR1预转载功能(需要CC1E=1才可写)
PWMA_CCMR2 |= 0x08;
PWMA_CCMR3 |= 0x08;
PWMA_CCMR4 |= 0x08;


PWMA_ARRH = (u8)(PWM_PERIOD >> 8); //设置周期时间
PWMA_ARRL = (u8)PWM_PERIOD;


PWMA_ENO = 0xFF;//使能输出(8位)
PWMA_PS= 0x00;//高级 PWM 通道输出脚选择位:PWM1_0&PWM2_0&PWM3_0&PWM4_0 //全部指向P1口8位


PWMA_BKR =0x80; //使能主输出
PWMA_CR1 |= 0x81; //使能ARR预装载,开始计时


EA=1;             //开总中断

while(1);   
}
//定时器0中断服务程序
void timer0() interrupt TMR0_VECTOR   //中断矢量号: 1
{
if(!PWM_Flag)
{ PWM_Duty++;
    if(PWM_Duty > PWM_PERIOD) PWM_Flag = 1;
}
else
{ PWM_Duty--;
    if(PWM_Duty <= 0) PWM_Flag = 0;
}
UpdatePwm();
}
//=========================================
// 函数: UpdatePwm(void)
// 描述: 更新PWM占空比.
// 参数: none. 返回: none.
//=========================================
void UpdatePwm(void)
{
    PWMA_CCR1H = (u8)(PWM_Duty >> 8); //设置占空比时间
    PWMA_CCR1L = (u8)(PWM_Duty);
    PWMA_CCR2H = (u8)(PWM_Duty >> 8); //设置占空比时间
    PWMA_CCR2L = (u8)(PWM_Duty);
    PWMA_CCR3H = (u8)(PWM_Duty >> 8); //设置占空比时间
    PWMA_CCR3L = (u8)(PWM_Duty);
    PWMA_CCR4H = (u8)(PWM_Duty >> 8); //设置占空比时间
    PWMA_CCR4L = (u8)(PWM_Duty);
}


//==== END =====================================



工程文件包,供初学单片机的爱好者参考。





浦江一水 发表于 2024-12-12 08:42:28

第六个实验是驱动0.96寸OLED单色显示屏
传统的STC89C52开发板,大多配有LCD1602接口。
它的倒序排列是:
GND VCC D7 D6 D5 D4 D3 D2 D1 D0 .....
恰好符合现在市面的一些小屏幕的引脚排列:

GND VCC SCL SDA RST DC CS .....

0.96寸OLED单色屏SS1306四针IIC驱动,分辨率为128*64。

0.96寸OLED单色屏SS1306七针SPI驱动,分辨率为128*64。
都符合这样的排列,因此对实验带来极大的便利。
本例实验是基于七针的OLED屏显示。且看:




主程序

//********************************************************************************
// 名称: Main.C
// 基于: 清翔 QX-MCS51_V3.0 开发学习板
// 实验: OLED 12864 显示屏 SSD1306 驱动
// 编程: 浦晓明(浦江一水) 2024-12-11
//********************************************************************************
#include "AI8051U.H"
#include "AI8051U_SYS.H"
#include "SSD1306.H"
#include "PIC.H"


/** 全局变量说明 设为全局,便于调试观察... **************/
//========================================================================
// 函数: voiddelay_ms(unsigned int ms)
// 描述: 毫秒级延时函数。
// 参数: ms,要延时的ms数,自动适应主时钟.
//=====================================================================
voiddelay_ms(unsigned int ms)
{ unsigned int i;
do{ i = MAIN_Fosc / 6000;
      while(--i);
    } while(--ms);
}
/**** 主函数入口 ************************/
void main(void)
{
SYS_Init();                         //系统初始化
OLED_Init();                      //初始化
OLED_CLS();                      //清屏
OLED_Light(0);                  //最低亮度
OLED_Light(0xFF);             //最高亮度
OLED_Light(0xCF);             //初始亮度
OLED_HZ16(10,3,"单",0,1); //测试汉字
OLED_HZ16(26,3,"片",0,1);
OLED_HZ16(42,3,"机",0,1);
OLED_A16(60,3,'O',0,1);      //测试8*16单字符
OLED_A08(68,4,'k',0,1);         //测试5*7 单字符
OLED_String(0,0,"AI8051U",0,1);//测试字符串...
OLED_Str5x7(10,6,"0123456789ABCD",0,1);
delay_ms(1000);
//主循环...
while(1)
{ OLED_BMP(0,0,128,8,OLED12864_IMG0,1); //显式BMP图
    delay_ms(2000);
    OLED_BMP(0,0,128,8,OLED12864_IMG1,1); //显式BMP图
    delay_ms(2000);
    OLED_CLS();                                       //清屏
    OLED_String(0,0,"单片机AI8051U",0,1);//中西文混合显示
    OLED_String(0,2,"单片机AI8051U",1,1);//正显/反显
    OLED_Str5x7(0,6,"AI8051U",0,1);         //正显
    OLED_Str5x7(0,7,"AI8051U",1,1);         //反显
    delay_ms(2000);
    OLED_CLS();                         //清屏
    OLED_LineH(10,10,108,0);   //画一水平线 作图算法...
    OLED_LineV(10,10,44,0);       //画一垂直线
    OLED_Line(10,10,118,54,0);   //两点一线 不同方向画线...
    OLED_Line(10,54,118,10,0);   //两点一线
    OLED_Line(118,54,118,10,0); //两点一线
    OLED_Line(118,54,10,54,0);   //两点一线
    OLED_Box(0,0,127,63,1);       //画一方框
    OLED_Circle(64,32,30,1);       //画一个圆
    delay_ms(2000);
}
}


工程文件包,供初学单片机的爱好者参考。




浦江一水 发表于 2024-12-13 16:05:44

第七个实验程序是关于驱动彩色显示屏的


有这么一个小屏,很袖珍,0.96寸的,外形体积与单色OLED相似,但是它却是TFT彩色显示屏。
分辨率是160*80,像素比128*64还多些。价格很便宜,RMB 6.7元。还带一个收藏盒,显得很精致玲珑。
试试看, 用这款传统的清翔QX-MCS51-V3.0开发学习板,来驱动它如何。


由上图可见,其引脚是排列是: GND VCC SCL SDA RST DC CS BLK ,
反过来,插在开发板的LCD1602的排母上,16脚对应GND,15脚对应VCC,就可以了。
经过实践,认识到:
这块显示屏舞动芯片是ST7735S, 经过显示屏厂商的二次开发,变成了模块,具有其一定的特殊性。
屏内的显示缓存,是按照128*160分辨率来设计的,所以显示屏的初始化和使用中,要注意X、Y坐标对应偏移和转换。
反色控制、RGB与BGR的格式切换,寄存器的设置,与手册描述稍有不同。
理论上,这屏旋转四个方向都可以使用的,本实验仅仅将其做成旋转270度横屏方向来使用。(其它方向需要局部修改程序)
实验已经成功驱动显示,其效果图:




其小视频:


主程序:

//===========================================================
// TFT 0.96" 彩色显示屏 160*80 实验程序
// 驱动: ST7735S
// 基于: AI8051U @ 清翔QX-MCS51_V3.0 实验板编程
// 整理:编程: 浦晓明(浦江一水) For 国芯论坛 2024-12-12
//===========================================================
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "AI8051U.h"
#include "AI8051U_SYS.h"
#include "LCD_7735.H"
#include "PIC.H"


unsigned char k;
char S;


void Delay1ms(void)        //@22.1184MHz
{ unsigned char data i, j;
_nop_();_nop_();
i = 22;        j = 128;
do
{ while (--j);
} while (--i);
}
void Delayms(unsigned int count)
{ unsigned int i;
for(i=0;i<count;i++)      
Delay1ms();            
}
//主程序入口
void main(void)
{ unsigned char i;
unsigned int wait=0;
SYS_Init();
LCD_Init();
while(1)
{
    for(i=0;i<16;i++)LCD_CLS(i);                  //16色清屏
    LCD_CLS(1);                                          //蓝色清屏
    LCD_H24(2,4,"**单片机**",12,1);         //24点阵中西文混合显示字符串
    LCD_A24(2,30,"A24_Ai8051U",15,1,11);   //显示12*24字符串
    LCD_H16(2,54,"H16_Ai8051U单片机",10,1); //16点阵中西文混合显示字符串
    LCD_A08(2,70,"A08_Ai8051U",15,1,10);    //显示6*8字符串
    Delayms(2000);
    LCD_CLS(4);                                          //暗红色清屏
    LCD_Point(5,5,15);                                 //画点测试
    LCD_LineH(5,5,50,10);                            //画水平线
    LCD_LineV(5,5,50,10);                            //画垂直线
    LCD_Line(5,5,55,55,10);                         //两点一线
    LCD_Box(60,10,80,40,15,0,3);                //画矩形方框(可指定边框色和填充色)
    LCD_A12(2,64,"A12_Ai8051U",10,4,11);//显示8*12字符串
    Delayms(2000);
    LCD_CLS(0);
    LCD_A16(2,0,"--LCD_ST7735_DEMO.--",10,0,20);
    LCD_BMP(14,16,64,64,P_SET);            //彩色图标显示
    LCD_BMP(82,16,64,64,P_ZER);
    Delayms(2000);
    LCD_CLS(0);
    LCD_A16(2,0,"--LCD_ST7735_DEMO.--",12,0,20);
    LCD_BMP(14,16,64,64,P_XSW);         //彩色图标显示
    LCD_BMP(82,16,64,64,P_SYS);
    Delayms(2000);
    LCD_CLS(0);
    for(i=0;i<16;i++)LCD_Box(i*10, 0,10,40,7,7,i);          //显示彩色条(色号0..15)
    for(i=0;i<16;i++)LCD_Box(i*10,40,10,40,7,7,(unsigned char)(i+16)); //显示彩色条(色号16..31)
    Delayms(2000);
}
}



工程文件包,供有兴趣的初学单片机爱好者参考。



men007 发表于 2024-12-16 17:53:06

{:4_174:}
页: [1] 2 3
查看完整版本: 当传统89C52单片机开发学习板遇到AI8051U(DIP40)时