laozhaoshiwoa 发表于 2025-1-9 03:18:58

第一次给Ai8051U核心板下载流水灯程序心得

贴心的销售给我发了一个Ai8051U核心板,上面已经焊好了最小系统,拿到手直接就能下载代码学习看效果,还给P2的8个引脚上都焊上了led灯,有了这些做个流水灯程序太方便了,在学习视频那里下载的还有源代码,改改就能用,说干就干,马上行动。



这是收到的核心板,上面的两个按钮要注意,下载代码时用到。


这个帖子里有学习视频和要下载的软件和代码


这里面是软件和代码


这里是要下载到单片机里的流水灯代码,选这个文件夹里的C语言-8bit就行


看着第二节课的视频教程安装好软件以后,双击这个文件在keil软件中打开这个项目


这是打开后的效果,在这里的第23行可以看到设置的值是24000000UL也就是24MHz,这是晶振的频率值,下载代码时输入用户程序运行时的IRC频率也要设置为24MHz。
因为核心板上的led灯是在P2引脚上,这个代码是P0引脚,所以要把代码里的P0都改成P2。


这些是修改后的效果,下面的P0全都改成P2,框里的值是参考P0的值改的,注释应该也是推挽输出的,我没改注释。


根据图上的顺序把编译输出的结果设置为hex格式。


点击箭头位置的全部编译按钮对代码进行编译,在下面的编译结果里可以看到创建了一个hex格式的文件,下面是0错误0警告,上面是数据的大小和代码的大小,一般编译后的文件都在这个项目的文件夹里。


这就是编译生成的那个hex文件,往单片机里下载文件就是选择这个文件。


红框里的内容都是要设置和修改的,因为核心板上的芯片型号是Ai8051U-34K64,所以芯片那里选择AI8051U-34K64。
因为这个程序是C语言-8bit,所以选择CPU指令那里要选择8-Bit。
因为程序中第23行定义的晶振频率是24000000UL,也就是24MHz,所以在输入用户程序运行时的IRC频率要设置为24MHz。
扫描串口位置目前是空白,需要先找一个Type-C口的数据线,一头连接电脑,一头插到核心板上,因为核心板上已经拷进去了流水灯代码,所以插到电脑上以后流水灯会运行。


这是插上后的效果
然后按下核心板上的P32 INTO按钮不松,再按一下POWER按钮,松开POWER按钮后再松开P32 INTO按钮,此时扫描串口那里会显示(HID1)USB-Writer,这说明可以往芯片里下载程序了,感觉这个芯片的供电电压是3.3v的,我直接插到5v的手机充电器上以后上面的VCC指示灯和POWER指示灯是一闪一闪的效果,大概5秒钟闪一次,不是常量的效果,里面的流水灯效果也不会有,插到电脑上就正常了。


按这两个按钮


这是设置好后的效果,可以看到扫描串口位置也显示正确了。
下一步是选择前面编译好的hex文件,点击上图里的打开程序文件按钮,然后选择C语言-8bit文件夹里的sample.hex文件并点击打开按钮




这是打开后的效果,可以看到程序文件中有内容了,下面也显示了打开的文件信息,可以确认一下,然后点击下载/编程按钮就可以往单片机里下载程序了。


这是下载成功后的效果,此时就可以在核心板上看到新的流水灯效果了,感觉这个流水灯比原来的流水灯速度慢了一些。
https://www.longxiangkeji.cn/shipin/liushuideng.mp4

laozhaoshiwoa 发表于 2025-1-9 22:45:58

第三节课:点亮一个LED灯
这里是用的Ai8051u核心板,上面已经焊上去的是P2口的8个LED灯,所以代码和视频中的代码有点不一样。
代码如下:
#include "ai8051u.h"


void main(void)
{
      P2M0=0x00;
      P2M1=0x00;
      P20=0;       //在这里为P20口赋值只执行一次,以后这个值都不会变。
      while(1)
      {
                P22=0;    //在这里为P22口赋值,会不停的执行这行代码,不停的给P22口赋值为0。
      }
}

因为是要点亮P2端口的LED灯,所以设置P2M0和P2M1的值。
在Ai8051u的手册中13.1.2章节可以看到PxM0和PxM1是x端口的模式配置寄存器,这里的x表示的是0到7的端口。


从这里可以看到P2M0和P2M1设置值其实是为P20M0到P27M0和P20M1到P27M1赋值。

P20M0到P27M0共8个二进制位,如果单独给它们设置0或者1,需要写8行代码,但是直接用P2M0赋值,只用1行代码就能解决,它的值对应下面的8个二进制位,也就是说可以是8个1或者8个0,或者有1又有0,具体根据情况设置,这里需要给P2引脚设置为准双向口模式,所以P2M0和P2M1都要设置为0,也就是P2M0下面的8个二进制位都是0,转换成十六进制就是0x00,所以P2M0和P2M1的值都是0x00。
0x表示值是十六进制,00表示两个4位二进制的值,个位的0表示低4位是0000;如果个位是F,就表示低4位是1111;如果个位是9,表示低4位的值是1001,这里表示把P2的第四个引脚设置为1,P2的第1、2和3引脚设置为0。十位的0表示的是高4位的值,也就是P2的第5、6、7、8个引脚,原理和低四位一样。

因为电流是从高电平流向低电平的,LED灯的正极连接的是电源,负极连接的是单片机P20到P27引脚,初始状态时,单片机的P20到P27引脚是高电平,和电源的电压一样,所以电流不会流动,LED灯就不会亮。当把P20引脚设置为0也就是低电平时,LED的负极和正极之间产生了电压差,这是时候电流流动了,所以LED灯也就亮了。

程序执行到while(1)外面时,那些代码只会执行一遍,进入死循环while(1)中以后,while(1)里面的代码就会被反复执行,不停的执行,所以可以根据需要将代码写在合适的地方。
最后看一下P20和P22引脚上的LED灯被点亮后的效果吧。


laozhaoshiwoa 发表于 2025-1-11 02:39:58

第四节课:USB不停电下载

当Ai8051U核心板插到电脑USB口上以后,在下载代码的软件中扫描串口那里不会显示插上去的核心板,需要先按住P32 INTO按钮不松,然后按一下POWER按钮,这时候扫描串口那里才会显示(HID1)USB-Writer,松开P32 INTO按钮后才可以点击下载/编程按钮往单片机里下载代码,每次下载都需要这样操作会很麻烦,所以这一节课使用代码实现不用按按钮也可以下载代码的功能。


首先在STC官网的软件工具——库函数——USB库文件——下载文件,把相关的文件先下载下来。


下载下来以后可以看到有两个文件夹,一个是库文件,另一个是范例程序


先把库文件中的stc32_stc8_usb.h复制到自己的项目中。


这个文件夹里有查询模式和中断模式,因为教学视频中是用的查询模式,所以先不考虑它们的区别,用查询模式就行。查询模式里有CDC库文件和HID库文件,CDC和HID库文件对应的是下载代码软件里收到用户命令后复位到ISP监控程序区里的两种模式。因为CDC更好用,所以这里选择CDC库文件。


在查询模式STC-CDC库文件夹中可以看到有4个lib后缀的文件,它们是Ai8051U所选的8位或32位模式对应的库文件,因为在编程软件中Device那里选择的是AI8051U-32Bit Series,也就是32位,所以这里选32位的库文件。


四个库文件


可以看到选择的是32位模式
在Target中可以看到Code Rom Size中有Large:64K program和Huge:64K functions, 16M progr.因为选择的是Large:64K program,不是Huge开头那个,所以这里选择库文件也不是包含Huge的那个,所以选择stc_usb_cdc_32.LIB库文件。


把这个库文件复制到自己的项目中,此时项目中有两个新加入的文件:stc32_stc8_usb.h和stc_usb_cdc_32.LIB。


在项目中添加stc32_stc8_usb.h头文件,在Source Group 1中添加复制过来的lib文件。




添加代码:#include "stc32_stc8_usb.h"后点击编译,可以看到main.c下面多了一个头文件。


打开范例程序文件夹中和库文件对应的demo项目,这里是查询模式STC-CDC范例程序\stc32g_cdc_query_demo。


可以看到这个项目的代码中也有#include "stc32_stc8_usb.h"加载头文件代码,项目文件中有一个stc_usb_cdc_32g.LIB文件。


把这个demo项目中的4、5、6行代码复制到自己的项目中。


这里第6行代码:char *USER_STCISPCMD = "@STCISP#";定义的值是下载软件里收到用户命令后复位到ISP监控程序区里的使用默认的内部自定义命令"@STCISP#",也可以把这个勾选去掉,然后在自定义命令里设置自己的命令。


把demo项目中的P_SW2 |= 0x80;代码复制到自己的项目的main()函数中。


P_SW2 |= 0x80;这句代码的意思是设置P_SW2特殊寄存器的B7位的值为1,因为0x80转换成二进制就是10000000,这里使用|=的意思是按位或赋值,如果直接用=会把其它位的值都改成0,可能会影响到一些功能,所以为了不影响其它位的值,就用|=,原来位置上是0,和赋值结果的0按位或以后还是0,如果原来位置上的值是1,和赋值结果的0按位或以后仍然保持值是1,这样就实现了只改指定位置的值的效果。

通过查询Ai8051U手册可以发现,P_SW2的名字是:串口2/3/4/I²C/比较器输出,功能脚切换控制,它的B7位名字是:EAXFR,功能是:扩展RAM区特殊功能寄存器(XFR)访问控制寄存器,值为0时,表示禁止访问XFR,值为1时表示允许访问XFR。这里需要将它设置为1。


把demo中的usb_init();和EA = 1;还有while (DeviceState != DEVSTATE_CONFIGURED);复制到自己的项目中。


usb_init();这个函数是写在stc_usb_cdc_32g.LIB文件中,因为lib文件不可访问,所以看不到这个函数的具体内容,直接用就行,要注意,这行代码一定要放在P_SW2 |= 0x80;代码后面,先开启访问XFR,然后调用这个函数进行各种操作。
EA = 1;这行代码的作用是开启EA总开关,需要开启以后,所有的小中断器被设置为1后才会起作用,从Ai8051U手册中可以看到所有的中断器都要经过EA中断器。



通过查询Ai8051U手册可以看到,EA是IE(中断允许寄存器)下的B7位的名字,EA=1就是把IE寄存器的B7位设置为1,所以这行代码和IE |=0x80;的效果是一样的,这两行代码用其中一行就行。


手册上的说明


写在代码里的效果

while (DeviceState != DEVSTATE_CONFIGURED);这行代码的意思是一直判断DeviceState != DEVSTATE_CONFIGURED这个条件是否不成立,当DeviceState==DEVSTATE_CONFIGURED时,这个判断不成立,跳出这个while循环,执行下面的语句。DEVSTATE_CONFIGURED在等待USB完成配置后,条件就会不成立。

最后把demo里的这几行代码也复制到自己项目的while(1)里的最上方,这几行代码的意思是不管接收到什么内容都原样发送回去。这一串代码还能实现一个不停电下载。


代码写完以后,就可以编译成二进制文件并准备下载了,编译后发现出现了很多警告信息L57,这里的意思是stc32_stc8_usb.h文件中有很多声明后的函数没有被使用,警告不会影响代码的执行,只是看着不好看。


消除警告的方法是:在编译设置那里选择L251 Misc里的disable Waming Numbers:里输入57,意思是屏蔽L57的警告。


编译后发现没有错误和警告了


编译成功后可以下载代码了,在下载软件的收到用户命令后复位到ISP监控程序区里的USB-CDC/串口模式选上,下面的三个选项勾上,打开程序文件那里选择刚编译好的文件。


特别注意,在下载软件的硬件选项中要设置对参数,因为项目里选择的单片机是32位的,所以选择CPU指令模式那里要选择32-Bit,上面的选项要勾上,芯片型号那里要选择正确,这里是AI8051U-34K64,因为没有用到定时器,所以输入用户程序运行时的IRC频率那里不用管它。

点击下载后,第一次使用这个功能还是需要按步骤按下两个按钮,以后再点击下载就不需要按那两个按钮了,扫描串口那里会自动识别为(HID1)USB-Writer,并下载代码,至此效果达到。




laozhaoshiwoa 发表于 2025-1-13 17:21:35

第五节:C语言基础
首先在上一个不断电下载那个项目里去掉stc32_stc8_usb.h文件中#define PRINTF_USB的注释//,这个是新版本中的代码,和视频中的会有点不一样。


在下面的代码中可以看到有代码:#define printfprintf_usb;这里的意思就是用printf来代替printf_usb函数,以后执行printf函数就是在执行printf_usb函数。


接着在代码中使用printf函数看输出效果,把printf函数写在代码usb_OUT_done();的前面,意思是当串口接收到数据后先把接收的数据显示出来,然后把printf函数中的内容显示出来。


编译代码并下载到单片机中。


因为选择的是USB-CDC/串口模式,所以查看输出信息也选择CDC/HID-串口助手,串口选择连接单片机的那个USB口,点击打开串口按钮,然后在发送缓冲区随便输入内容, 点击发送数据按钮,这时就可以在接收缓冲区看到返回的内容。


这里可能是因为编码的原因,导致张三的三字和Y合并成了别的字,先不管它。
可以看到接收缓存区有一个输入的内容,如果不想显示这个,可以在更多设置里把显示发送的数据关掉。去掉了以后,接收缓存区里已经没有红色的那行数据了。


在代码中printf里的末尾有\r\n,这个是换行符,输出数据时看到这个符号就会换行,在接收缓存区的数据里看到确实换行了。
代码里的USB_SendData(UsbOutBuffer,OutNumber);是把发送的数据原样显示,可以看到张前面有个1,就是发送的内容,不想显示这个可以把这行代码注释掉。


注释掉以后再发信息可以看到接收后面没有那个发送数据1了。


printf_usb()函数的原型是:int printf_usb(const char *fmt,……);
其中参数fmt是格式控制字符串,它包含了两种类型的对象,可以是普通字符和转换说明。
当它是普通字符时,双引号里的内容原样输出。比如:printf("张三是我");里面的字符串是普通字符,可以原样输出。
当它是转换说明时,比如printf("我的名字是:%d","张三"),里面的%是转换符号的开头,后面的字符是转换类型,如果是转换说明,里面有几个转换说明后面就需要有几个参数,这里的"张三"就是后面的参数,用来填充第一个参数里的转换字符。最后显示的内容就是:我的名字是:张三。如果要原样输出%,需要写成%%,如printf("10%%3的值是:$d",10%3);这里显示的结果就是:10%3的值是:1。


显示的效果:


如果想查看显示的结果的十六进制结果,可以勾选上接收缓冲区的选项,这样右侧会出现一个十六进制的结果。


这里的zhang san最后被转换为7A 68 61 6E 67 20 73 61 6E,这里的z的Ascii码是122,122转换成十六进制就是7A,转换方式122/16=7余10,10在十六进制里就是A,所以是7A。


%后面可以跟标志信息,用来对结果进行处理,如n,m中n表示小数点前面有几位数,m表示小数点后面有几位数。标志信息后面还可以用很多格式字符表示不同的格式,如:%3,2f表示小数点前面3位,小数点后面2位的小数。
十进制、十六进制、二进制数之间的转换方式要掌握,后面用到的很多。
数据类型主要有整型 int、浮点型 float、字符型 char、字节类型 bit,掌握它们的区别和范围。
有的数据类型太长,可以用define来定义一个别名,比如:#define u8 unsigned char 这时候u8就可以当成unsigned char使用了,使用时是u8 zifu;
unsigned 是无符号的意思,它的值都是正数,不加这个关键字时,值可以是负数,也可以写成signed定义变量是有符号的。

C语言的常用运算符是+、-、*、/、%、++、--。++和--是数字的自增和自减。


关系运算符:>、<、==、>=、<=、!=、主要用在判断的位置,0为假,非0为真。


判断结果:


逻辑运算符:&&(与)、||(或)、!(非)。
&&两边的条件都为真时,整个表达式才是真,有一个条件为假时,整个表达式都是假。
||两边的条件有一个为真,整个表达式就是真,两边条件都是假时,整个表达式才是假。
!右边的条件为真时表达式就是假,右边条件为假时表达式为真。
位运算符:&、|、^、-、<<、>>。
这些运算符是针对二进制位的。
&两个二进制数,相同位都为1时,结果为1,有一个为0时,结果为0。
|两个二进制数,相同位有一个是1时,结果为1,都为0时,结果为0。
^两个二进制数,相同位值一样时,结果为0,否则为1。
-一个二进制数,为1的位置变成0,为0的位置变成1。
<<一个二进制数整体向左移动指定位数,末尾补0,超过8位的高位部分去掉。
>>一个二进制数整体向右移动指定位数,高位补0,超过8位的低位部分去掉。
表达式?a:b;这个是三元运算符,如果表达式成立,则返回冒号左侧的值,如果不成立就返回右侧的值。
.或->用来引用类、结构体和公用体成员。
&取址运算符,用来返回变量的存储地址。
*指针运算符,用来指向变量的存储地址。
,逗号运算符,会按顺序执行每个逗号前面的运算。
溢出就是结果超出了变量的取值范围后出现了不符合要求的结果,所以要确认结果范围,使用合适的变量类型。

laozhaoshiwoa 发表于 2025-1-13 21:12:55

第六节:I/O输入输出

GPIO就是通用输入/输出端口,通俗的说就是一些引脚可以通过它们输出高低电平,或者通过它们读入引脚的状态是高电平还是低电平。
高电平就是指引脚上的电压无限接近电源正极的电压,如果电源的电压是3.3v,引脚上的电压也是3.3v就是高电平,用1表示高电平。
低电平就是指引脚上的电压无限接近GND的电压,也就是0v,用0表示低电平。
如果单片机的vcc电源正极是3.3v,可以给引脚上接5v电源吗?不能,引脚上的电压不能超过vcc电压0.3v,也就是vcc电压是3.3v时,引脚上的电压不能超过3.6v。
Ai8050U的引脚有四种模式:
模式1:PnM1=0;PnM0=0;准双向口模式(传统8051端口模式,弱上拉),灌电流可达20mA,拉电流为270-150uA。灌电流是从vcc到IO引脚的电流。拉电流是从单片机引脚到GND的电流。
模式2:PnM1=0;PnM0=1;推挽输出模式(强上拉输出,可达20mA,需加限流电阻)。
模式3:PnM1=1;PnM0=0;高阻输入模式(电流既不能流入也不能流出)。
模式4:PnM1=1;PnM0=1;开漏模式,内部上拉电阻断开,该模式既可读取外部状态,也可对外输出(高电平或低电平),如果要正确读取外部状态或需要对外输出高电平,需外加上拉电阻,否则读取不到外部状态,也不能对外输出高电平。

实验箱用的是3.3v供电。
往引脚输入低电平或低电平需要根据施密特触发是否打开来决定,如果输入低电平,打开施密特触发时最大值0.99v,关闭施密特触发时最大值是1.07v。
往引脚输入高电平时,打开施密特触发时最低1.18v,关闭施密特触发时最低1.09v。默认上电后,施密特触发是打开状态。
按键输入检测:
核心板上有两个引脚连接了按键,分别是P3.2和P3.3引脚,按键的一端连接单片机IO口,另一端连接GND,如果要检测按键是否按下,只需要检测按键连接的IO口是高电平还是低电平即可,高电平是1,低电平是0,如果按键按下,单片机引脚和GND连接,此时引脚是低电平,用if(P32==0)来判断。

任务一:按下P3.2引脚上的按钮后P2.2引脚上的LED灯亮,松开P3.2引脚上的按钮后,P2.2引脚上的LED灯熄灭。


效果:


任务三:按下P32按钮后P22端口LED灯亮,再次按下P32按钮后P22端口LED灯灭
软件延时代码生成方式:参数要设置好。

任务三代码部分。



课后任务一:按下P32按钮后P22灯亮,按下P33按钮后P22灯灭。

课后任务二:第一次按下P32按钮后亮一个灯,第二次按下按钮亮两个灯,直到8个灯都亮。

至此,按钮的使用已经完成。


laozhaoshiwoa 发表于 2025-1-15 00:43:37

第七节课:定时器中断

在第六节课里使用软件延时的方式来实现等待指定时间的效果,用这种方式的缺点就是执行到那里时,不停的执行空代码,不会继续向下执行,这就导致效率很低,所以这里用定时器中断的方式处理延时效果。
MCU是单核的,同一个时间只能执行一件事,没有特殊的情况不能被打断,所以这里要引入一个特殊的情况——定时器中断,中断就是打断正在做的事情。
定时器作用:
1、用于计时系统,可实现软件计时,或者使程序每隔一段固定时间完成一项操作。
2、替代长时间的delay;提高程序的运行效率和处理速度(可以打断主循环)。
任务一:LED灯三秒取反一次,在任意时间按下按钮后,串口中要打印出来按下的次数。
先在下载代码的软件里找到定时器计算器标签,在里面设置好几个参数后会自动生成定时器代码,因为Ai8051u用的定时器是24MHz的晶振,所以这里选24MHz,下面的使能定时器中断也勾上。


以上代码中第1到3行是中断函数,触发中断后要执行的代码就放在这里。5到15行是定时器设置代码,这里设置的是3秒,设置的中断间隔也是3秒,3秒时触发中断函数。
任务代码:




显示的效果部分:


关于乱码汉字的原因及查询,在AI8051U.pdf文档的“附录J:关于Keil软件中0xFD问题的说明”中有具体介绍。


定时器的应用:
在代码中有TM0PS = 0x5B;这个是用的TM0PS,也就是定时器0的8位预分频寄存器,定时器0是从一个系统时钟过来的,定时器其实是一个计数器,它能从0数到65536然后溢出,24M是1微秒计数24次,如果觉得速度太快,还可以分频,公式是:定时器0的时钟=系统时钟SYSclk÷(TM0PS+1),如果TM0PS的值为0,则系统时钟除以1还是自己,没有变化。
TM0PS后面还有÷12和÷1,如果感觉计数还是太快,可以继续分频,÷12意思就是系统时钟计数12次,定时器0才计数1次。12T模式就是CPU时钟12分频,也就是FOSC/12,这里的FOSC是系统时钟除以TM0PS+1后的值。


这部分的代码作用和设置的值可以结合AI8051U.pdf文档中的指定位置的资料进行理解。






函数的定义和调用:
函数的定义:包含返回值、函数名和入口参数,并定义了函数具体功能,名字要和功能相符合,让别人看到名字就知道这个函数的作用是什么,函数名不能是关键字,不能是奇怪的符号。


这是一个函数,返回值类型是int,函数名是add,入口参数有两个分别是int类型的a和int类型的b,return c;是返回int类型的结果,{}中间的内容是函数体,里面是这个函数的具体内容。
int add(int a,int b);    //这一行代码是对这个函数的声明,因为代码是从上往下执行的,如果一个函数写在main()函数后面了且没有声明,在main()函数中执行到这个函数时就会报错,如果在main()函数前声明了,就不会报错。
add(1,2);    //这一行代码是调用这个add函数,里面的1和2是两个入口参数,也是int类型,要和定义函数时要求的数据类型一样。
任务二:按下按钮后灯亮,等待三秒后灯灭。


任务三:按下按钮后两个灯交替点亮(间隔500毫秒),再次按下按钮后两个灯熄灭。


课后小练:





显示效果:



zhaoye818 发表于 2025-1-15 07:19:41

必须点赞

tianjing818 发表于 2025-1-15 07:22:43

{:qiang:}

laozhaoshiwoa 发表于 2025-1-15 10:08:59

zhaoye818 发表于 2025-1-15 07:19
必须点赞

谢谢{:aixin:}

laozhaoshiwoa 发表于 2025-1-15 10:09:17

tianjing818 发表于 2025-1-15 07:22


谢谢{:ciya:}
页: [1] 2
查看完整版本: 第一次给Ai8051U核心板下载流水灯程序心得