实验箱已收到!看冲哥《STC32位8051单片机视频教程》学习记录
本帖最后由 tyun1990 于 2023-10-27 18:38 编辑学习记录(第一课、第二课)
很感谢能看到冲哥的基于51的STC 32位视频教程,让我们了解STC32G可以应用在哪些领域,比如娱乐类产品、家庭生活类产品(热水器、开水瓶、电热水器、电磁炉)、电子体温计、医疗、工业控制等等用途,当然单片机的用途不简简单单用于上述产品,我呢本身是学电子信息工程的。也很热爱这个行业,通过冲哥的介绍也让我对STC32G新系列的更深层次的认知。STC32G有很多地方也让我很为之欣然,比如它可以和STC8H一样可以USB下载和USB仿真,有128K程序存储区,有CAN总线、LIN总线、全速USB,还集成了多种数字外设:SPI、I2C、2个高速异步串口,RTC。另外还支持8080和6800两种驱动方式的LCD驱动模块;还支持超高速12位高精度ADC,还有5个16位定时器、49个中断源,这在很多产品中很实用。
我呢,也同样借鉴冲哥第一课的问题解答,新手学51还是STM32呢,我也想再说一句,新手直接选择STC32G比选择89C52、89S52要好很多,很多寄存器支持位寻址,速度也快,虽然性能相比STM32稍逊,但有很多方面是STM32所不及的,比如STC32可以简单的程序就可实现简单的控制,但STM32要复杂环境配置、库函数以及复杂端口配置,STM32的复杂程度远比51单片机复杂。还有STC32可以和STC8H程序可以相兼容哦!
单片机技术也是现代电子系统设计、智能控制的核心技术,随着时代的发展慢慢的成了普通人的必修课。
为了学习STC8H、STC32G我也花费了很多精力和时间,也买了一些书籍用来学习,每天刻苦学习。也买了很多模块用来学习
我也很希望有更多的同学、同志们一起学习32位8051单片机,我“开源”了一种调试程序的方法。在“8楼”链接上
压缩包包含 .C 、 .H 文件和实例工程,方便没有试验箱的同学们一起学习,少走弯路
学习记录(第三课)
STC-ISP V6.92D新版下载 https://www.stcai.com/gjrj【新版下载】2023年10月
Keil C51和C251下载 https://www.keil.com/download/product/【新版下载】2023年10月
注意:如果还要用到传统89C52,需要先安装Keil C51再安装Keil C251 哦!不过不推荐先学STC89C52在学STC32G12K128
STC-ISP添加仿真文件到Keil
三、多种下载方式
【USB直接下载】
通过usb转串口IC:CH340、 PL2303-GL
注意:(USB 转串口芯片的发送脚一般都是强推挽输出,必须在目标芯片的 P3.0 口和 USB 转串口芯片
的发送脚之间串接一个二极管,否则目标芯片无法完全断电,达不到给目标芯片停电的目标。)
学习记录(第四课) 发光二极管(LED)原理,通过STC32G_GPIO如何点亮LED发光二极管
#include "COMM/stc.h"#include "COMM/usb.h" char *USER_DEVICEDESC=NULL;char *USER_STCISPCMD="@STCISP#";char *USER_PRODUCTDESC=NULL; sfr P4=0xc0; //LED灯组使能sfr P4M1=0xB3; //P6 口配置寄存器 1sfr P4M0=0xB4; //P4 口配置寄存器 0 sfr P6=0x80; sfr P6M1=0xcb; //P6 口配置寄存器 1sfr P6M0=0xcc; //P6 口配置寄存器 0 //sbit P40=P4^0; //Q11_B//sbit P60=P6^0; //LED4控制//sbit P61=P6^0; //LED5控制 void main(){ P4M1=0x00;P4M1=0x00;//定义P4为双向IO口 P6M1=0x00;P6M0=0x00;//定义P6为双向IO口 usb_init(); EA=1; while(1) { P4=0x00;//LED灯组低电平使能 P6=0xFC;//将P6.0,P6.1灯点亮 }} 编程技巧:可以通过自定义类型的变量,比如:#define uchar unsignedchar或者typedef unsigned char uchar下面写程序的时候就可以简写成 ucahr temp;
学习记录(第五课)一、Printf函数应用:if(DeviceState !=DEVSTATE_CONFIGURED) //判断USB是否连接成功? continue: if(bUsbOutReady) //接收一组数据后通过printf()输出一组数据 { usb_OUT_done(); printf("HelloWorld!");//输出Hello world!给计算机 } Printf函数还可以这样用: printf("Hello World!"); printf("室内温度:%.2f\r\n",11.2);//打印数据关于printf用到的特殊格式:
二、C语言常见运算符一、算术运算符+加-减 *乘 /除%模(余)运算符(取模运算例如:265%10则结果是5,舍去小数前面的数,265%100则是65 )++i,-i,i++,i- 自增
(注意++i和i++是有区别的,++i是先进行加1运算后赋值)二、位运算符>>右移<<左移 &按位与 |按位或 ^按位异或 ~取反三、赋值运算符+=加赋值-=减赋值*=乘赋值/=除赋值%=求余赋值&=按位与赋值|=按位或赋值^=按位异或赋值<<=左移位赋值>>=右移位赋值三、数据类型 数据类型 长度 值区域 Bit 1 0,1 Unsigned char 8 0~255 Char(signedchar) 8 -128~127 Unsignedint 16 0~65536 Int(signedint) 16 -32768~32767 Unsignedlong 32 0~4 294 967 295 Long(signedlong) 32 -2 147 483 648~2 147483 647 Float 32 +-1.175494E-38~+-3.402823E+38 Sbit 1 0,1 Sfr 8 0~255 Sfr16 16 0~65535 学习记录(第六课)
一、Delay函数应用#define MAIN_Fosc 24000000UL
void Delay_ms(u16 ms) //定义一个无符号整型变量ms{ u16 i;//定义一个无符号整型变量i do { i=MAIN_Fosc/6000;//系统时钟的6分频 while(--i); }while(--ms);} 二、模块化编程:优点:可维护性、灵活架构、方便模块间组合分解、多人协作互不干扰、方便移植
模块化编程”.H”文件结构#ifndef __MATH_H //if no define #define __MATH_H #define MAIN_Fosc 24000000ULvoid Dela_ms(u16 ms);//函数在中间位置进行声明,声明后才可以外部调用 #endif 模块化编程”.C”文件结构#include "STC32.H"//头文件#include "STC8H.H"//头文件 void Delay_ms(u16 ms) //定义一个无符号整型变量ms{ u16 i;//定义一个无符号整型变量i do { i=MAIN_Fosc/6000;//系统时钟的6分频 while(--i); }while(--ms);} 学习记录(第七课)按键控制LED灯按键的种类有轻触开关、自复位、自锁,封装有贴片、直插等等常见的轻触开关内部结构:由下图可见,是有塑胶按钮、窝仔片、金属触点等组成。按下后就导通,松手触点就断开。
但要想在单片机中控制就不可避免的出现抖动问题,消抖就的方式常见有2种,一种是利用硬件消抖,还有一种是在软件写延时程序多次判断按键是否按下来进行软件消抖。
/***** main主函数 *****/#include <STC32G.H>#include "KEY.H"#include "DELAY.H"
unsigned char Key_Num;void sys_init();
void main(){ unsigned char LED_DATA=0XFE; sys_init(); P6=LED_DATA; while(1) { // /***** 上升沿动作 *****/// Key_Num=Key();// if(Key_Num==1)// {// P40=0;//LED灯组低电平使能// P60=!P60;//// while(Key_Num==1);// } /***** 下降沿动作 *****/ Key_Num=Key(); if(Key_Num==1)//判断P34口按键按下? { P40=0;//LED灯组低电平使能 LED_DATA=((LED_DATA<<1)+1); if(LED_DATA==0xFF) LED_DATA=0xFE; P6=LED_DATA; while(Key_Num==1); } if(Key_Num==2)//判断P35口按键按下? { P40=0;//LED灯组低电平使能 P61=0;//将P6.1灯点亮 } }}
void sys_init(){ WTST = 0;//设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快 EAXFR = 1; //扩展寄存器(XFR)访问使能 CKCON = 0; //提高访问XRAM速度
P0M1 = 0x00; P0M0 = 0x00; //设置为准双向口 P1M1 = 0x00; P1M0 = 0x00; //设置为准双向口 P2M1 = 0x00; P2M0 = 0x00; //设置为准双向口 P3M1 = 0x00; P3M0 = 0x00; //设置为准双向口 P4M1 = 0x00; P4M0 = 0x00; //设置为准双向口 P5M1 = 0x00; P5M0 = 0x00; //设置为准双向口 P6M1 = 0x00; P6M0 = 0x00; //设置为准双向口 P7M1 = 0x00; P7M0 = 0x00; //设置为准双向口} /***** Key.c *****/ #include <STC32G.H>#include "DELAY.H"unsigned char Key(){ unsigned char KeyNumber=0; if(P34==0){Delay_ms(10);while(P34==0);Delay_ms(10);KeyNumber=1;}//延时消抖再判断P34按键是否按下,按下KeyNumber返回1 if(P35==0){Delay_ms(10);while(P35==0);Delay_ms(10);KeyNumber=2;}//延时消抖再判断P34按键是否按下,按下KeyNumber返回2
return KeyNumber;}/***** Key.h *****/
#ifndef _KEY_H_#define _KEY_H_
unsigned char Key();
#endif/***** Delay .c *****/
#include <STC32G.H>
#define MAIN_Fosc 24000000ULvoid Delay_ms(unsigned int ms) //定义一个无符号整型变量ms{ unsigned int i;//定义一个无符号整型变量i do { i=MAIN_Fosc/6000;//系统时钟的6分频 while(--i); }while(--ms);}/***** Delay.h *****/
#ifndef _Delay_ms_H_#define _Delay_ms_H_
void Delay_ms(unsigned int xms);
#endif
学习记录(第八课)蜂鸣器原理与驱动笔记:蜂鸣器驱动分有源和无源,有源就是内置驱动器,无源就需要单片机输出内置脉冲驱动。有源蜂鸣器驱动程序和LED灯驱动程序差不多。
我们的开发板为有源蜂鸣器程序思路:K1(P3.2)按下→P5.4口输出低电平,ss8550导通,蜂鸣器得到VCC电压,得以鸣响
#define KEY1 P32 //定义一个按键 引脚选择P32#define BEEP P54 //定义一个按键 引脚选择P54
通过以上程序定义P3.2口为按键1,P5.4口为蜂鸣器驱动端口
if( KEY1 == 0) { delay_ms(10); if(KEY1 == 0 ) { BEEP=!BEEP;// 如果KEY1再次按下BEEP取反,打开或者关闭蜂鸣器}}分析上面的程序,单key1按下后延时10毫秒消抖,然后再判断key1是不是还是按下的,如果还是按下的就执行BEEP=!BEEP;语句一次。 本帖最后由 tyun1990 于 2023-10-20 22:51 编辑
打卡!打卡!第九课、第十课交作业啦
第十课作业另发新帖:
--------------------------------------------------------------------------------
干货!干货!基于TM1638数码管、按键、8位LED程序分享
https://www.stcaimcu.com/forum.php?mod=viewthread&tid=4704
(出处: 国芯论坛-STC全球32位8051爱好者互助交流社区)
--------------------------------------------------------------------------------
有个地方忘记说了,必须要在主程序对模块“初始化”!
void main()
{
TM1638_Init(5); //5表示亮度为5,中高亮度
while(1)
{
}
}
void AddrDisPlay(unsigned char Addr,unsigned char Data)
{
TM1638_WriteCmd(0x44);
if(Addr==1){TM1638STB = 0;TM1638_WByte(0xC0);TM1638_WByte(Data);TM1638STB = 1;}
if(Addr==2){TM1638STB = 0;TM1638_WByte(0xC2);TM1638_WByte(Data);TM1638STB = 1;}
if(Addr==3){TM1638STB = 0;TM1638_WByte(0xC4);TM1638_WByte(Data);TM1638STB = 1;}
if(Addr==4){TM1638STB = 0;TM1638_WByte(0xC6);TM1638_WByte(Data);TM1638STB = 1;}
if(Addr==5){TM1638STB = 0;TM1638_WByte(0xC8);TM1638_WByte(Data);TM1638STB = 1;}
if(Addr==6){TM1638STB = 0;TM1638_WByte(0xCA);TM1638_WByte(Data);TM1638STB = 1;}
if(Addr==7){TM1638STB = 0;TM1638_WByte(0xCC);TM1638_WByte(Data);TM1638STB = 1;}
if(Addr==8){TM1638STB = 0;TM1638_WByte(0xCE);TM1638_WByte(Data);TM1638STB = 1;}
}
上面这个程序解释:
如果Addr收到1,表示在第一个数码管显示1位数;发送数据前必须先使能模块,所以TM1638STB=0;然后再发送地址,再发送要显示的数据;最后关闭使能。
数据命令
本帖最后由 tyun1990 于 2023-10-20 21:41 编辑
学习记录(第十一课)
这是根据冲哥老师《STC32位单片机教程》改编的。再此说明一下,我为什么没有照抄老师的程序,因为我没有老师同等的实验箱,只能举一反三,参照老师的部分程序更改。有做的不好的地方请老师、坛主、同学们指正{:lol:}
这也是我为什么下午在论坛公布了我的“TM1638数码管+按键+LED模块”的驱动程序。同时也希望我能融入大家庭,跟大家一起学习一起成长{:lol:}
main主函数
#include <STC32G.H>
#include "TM1638.H"
#include "intrins.h"
unsigned char KeyNum=0,SMG_WEI=2,Tube; //KeyNum用来读取按键值,SMG_WEI用来在哪一个数码管位显示,Tube为数码管当前值
unsigned char LED_Num=0,LED_SW; //LED_Num用来操作哪一个LED灯,调节范围0-7。LED_SW用过来操作LED灯亮还是灭,0为灭1为亮
unsigned char LianDu=0; //LianDu用来条件数码管和LED灯的亮度,调节范围0~5,上电默认最低亮度
unsigned char Show_Tab1,Show_Tab2,Show_Tab3,Show_Tab4;
unsigned long TimCount=0; //计数单位1ms
bit RUN_State = 0; //开始运行/结束运行
unsigned char num = 0;
void Timer0_Init(void) //1毫秒@24.000MHz
{
AUXR &= 0x7F; //定时器时钟12T模式
TMOD &= 0xF0; //设置定时器模式
TL0 = 0x30; //设置定时初始值
TH0 = 0xF8; //设置定时初始值
TF0 = 0; //清除TF0标志
TR0 = 1; //定时器0开始计时
ET0 = 1; //使能定时器0中断
}
void main()
{
P0M0=0x00;P0M1=0x00;
TM1638_Init(5); //TM1638初始化
// AddrDisPlay(8,0x5b); //测试用,在第8为显示数字2
Timer0_Init();
EA = 1;
while(1)
{
KeyNum=TM1638_keyscan(); //读取按键值
if(KeyNum==0) //如果S1按下就加1,长按累加
{
Delay(20);
if(KeyNum==0)
{
if(RUN_State)
TimCount = 0;
RUN_State = !RUN_State;
}
}
AddrDisPlay(1,TubeTab); //刷新
AddrDisPlay(2,TubeTab); //刷新
AddrDisPlay(3,TubeTab); //刷新
AddrDisPlay(4,TubeTab); //刷新
// AddrDisPlay(SMG_WEI,TubeTab); //更新数码管显示 AddrDisPlay(第几位,显示几);
}
}
void Timer0_Isr(void) interrupt 1
{
if( RUN_State==1 ) //如果开始运行
{
TimCount++; //每隔1ms+1
Show_Tab1 = TimCount/10000%10;
Show_Tab2 = TimCount/1000%10;
Show_Tab3 = TimCount/100%10;
Show_Tab4 = TimCount/10%10; //取10位
}
}
TM1638.C
#include <STC32G.H>
#include "TM1638.H"
#define MAIN_Fosc 24000000UL //定义主时钟
//段位 1 2 3 4 5 6 7 8
unsigned char code TM1638_TubeAddrTab[] ={0xC1,0xC3,0xC5,0xC7,0xC9,0xCB,0xCD,0xCF};
/**** 延时程序 *****/
//n为延时时间
//无返回值
void Delay(unsigned int ms)
{
unsigned int i;
do
{
i = MAIN_Fosc/6000;
while(--i);
}while(--ms);
}
/**** TM1638写一个字节 *****/
//CMD为要写入的值,主程序不需要调用
//无返回值
void TM1638_WByte(unsigned char cammand)
{
unsigned char i;
for (i = 0; i < 8; i++)
{
TM1638CLK = 0;
TM1638DIO = cammand & 0x01;
cammand >>= 1;
TM1638CLK = 1;
}
}
/**** TM1638读一个字节 *****/
//有返回值,主程序不需要调用
unsigned char TM1638_ReadByte()
{
unsigned char temp,i;
temp = 0;
TM1638DIO = 1;//释放数据线
for (i = 0; i < 8; i++)
{
temp >>= 1;
TM1638CLK = 0;
TM1638CLK = 1;
if (TM1638DIO) temp |= 0x80;
}
return temp;
}
/**** TM1638清屏函数 *****/
//无返回值,主程序可以调用
void TM1638_clear()
{
unsigned char i;
TM1638STB = 1;
TM1638STB = 0;
TM1638_WByte(0x40);
for (i = 0; i < 16; i ++)
TM1638_WByte(0x00);//在所有16个地址中写入0x00
TM1638STB = 1;
TM1638STB = 0;
TM1638_WByte(0xc0);//设置显示地址为0x00
TM1638STB = 1;
}
/**** TM1638写命令 *****/
//无返回值,主程序需要调用
//cmd写显示命令:
void TM1638_WriteCmd(unsigned char cammand)
{
TM1638STB = 0;
TM1638_WByte(cammand);
TM1638STB = 1;
}
/**** TM1638设置亮度 *****/
//TM1638_SetBrightness
//设置亮度,共8级,0~7。亮度0x88~0xFF范围内可调,FFh最大亮度
//注意:0x88为开显示
//主程序不需要调用
void TM1638_SetBrig(unsigned char Brightness)
{
TM1638_WriteCmd(0x88|Brightness);
}
/**** TM1638设置自动增加模式 *****/
//示例:Auto_AddrDisplay(0x5b)//显示2
void Auto_AddrDisplay(unsigned char Data)
{
TM1638STB = 0;
TM1638_WByte(0x40);
TM1638_WByte(Data);
TM1638_WByte(0x00);
TM1638STB = 1;
}
/**** TM1638设置亮度 *****/
//LianDu参数范围0~7
//主程序需要调用
void TM1638_Init(unsigned char LianDu)//亮度共8级,0~7。
{
TM1638_clear();
TM1638_SetBrig(LianDu);//亮度从0x88~0xFF范围类可调
}
/**** TM1638指定地址模式 *****/
//Addr:1~7范围显示
//Data:要显示的数据
//示例1:AddrDisPlay(3,0x5B)//第3个数码管显示2
//示例2:AddrDisPlay(1,TubeTab);//第1个数码管显示TubeTab表内的数
void AddrDisPlay(unsigned char Addr,unsigned char Data)
{
TM1638_WriteCmd(0x44);
if(Addr==1){TM1638STB = 0;TM1638_WByte(0xC0);TM1638_WByte(Data);TM1638STB = 1;}
if(Addr==2){TM1638STB = 0;TM1638_WByte(0xC2);TM1638_WByte(Data);TM1638STB = 1;}
if(Addr==3){TM1638STB = 0;TM1638_WByte(0xC4);TM1638_WByte(Data);TM1638STB = 1;}
if(Addr==4){TM1638STB = 0;TM1638_WByte(0xC6);TM1638_WByte(Data);TM1638STB = 1;}
if(Addr==5){TM1638STB = 0;TM1638_WByte(0xC8);TM1638_WByte(Data);TM1638STB = 1;}
if(Addr==6){TM1638STB = 0;TM1638_WByte(0xCA);TM1638_WByte(Data);TM1638STB = 1;}
if(Addr==7){TM1638STB = 0;TM1638_WByte(0xCC);TM1638_WByte(Data);TM1638STB = 1;}
if(Addr==8){TM1638STB = 0;TM1638_WByte(0xCE);TM1638_WByte(Data);TM1638STB = 1;}
}
/**** TM1638键盘扫描 *****/
//有返回值
//主程序需要调用
unsigned char TM1638_keyscan()
{
unsigned char i,temp;
TM1638STB = 0;
TM1638_WByte(0x42);
for (i = 0; i < 4; i++)
temp = TM1638_ReadByte();
TM1638STB = 1;
for (i = 0; i < 4; i++)
{
if (temp == 0x01 || temp == 0x10)
return ((temp == 0x01) ? i:(4+i));
}
return 8;
}
/**** LED显示和熄灭 *****/
//主程序需要调用
//LED_Port(第几个LED灯,0为灭1为亮);
void LED_Port(unsigned char Temp,unsigned char LED_EN)
{
TM1638_WriteCmd(0x44);
TM1638STB = 0;
if(LED_EN==1)
{
TM1638_WByte(Temp);TM1638_WByte(0x01);//点亮
}
else if(LED_EN==0)
{
TM1638_WByte(Temp);TM1638_WByte(0x00);//熄灭
}
TM1638STB = 1;
}
TM1638.H文件
#ifndef _TM1638_H_
#define _TM1638_H_
sbit TM1638STB = P0^2;
sbit TM1638CLK = P0^1;
sbit TM1638DIO = P0^0;
static unsigned char TubeTab[] = {
// 1 2 3 4 5 6 7 8 9
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,
// 9 A B C D E F清0
0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};//0~F
void TM1638_Init(unsigned char LianDu);//亮度共8级,0~7。
unsigned char code TM1638_TubeAddrTab[];
void Delay(unsigned int n);
void TM1638_WriteCmd(unsigned char cmd);//写命令
void TM1638_clear();
void TM1638_SetBrig(unsigned char Brightness);//亮度调节unsigned charBrt=0~7
void Auto_AddrDisplay(unsigned char Data);//自增加位,新数在旧数之后
void AddrDisPlay(unsigned char Addr,unsigned char Data);//指定位显示一个数
unsigned char TM1638_keyscan();//键盘扫描键值
void LED_Port(unsigned char temp,unsigned char state);
#endif
学习记录(第十二课)
课堂笔记
TMOD = 0x50; //设置计数器模式,16位不自动重载模式
TL1 = 0x00; //设置计数初始值
TH1 = 0x00; //设置计数初始值
TF1 = 0; //清除TF1标志
TR1 = 1; //定时器1开始计时
ET1 = 1; //使能定时器1中断
P3PU = 0x20; //打开内部上拉4.1K
void Timer0_Isr(void) interrupt 1
{
TimCount++; //每隔1ms+1
if( TimCount>=2000 ) //2秒
{
TimCount = 0;
Count_T1 = (TH1 *256 )+ TL1; // 定时器高八位和第八位合并一起
TH1 = 0;
TL1 = 0;
Show_Tab = Count_T1/1000%10;
Show_Tab = Count_T1/100%10;
Show_Tab = Count_T1/10%10;
Show_Tab = Count_T1/1%10; //取10位
}
SEG_Fre(); //数码管刷新的
}