当前位置: 首页 > news >正文

建站哪家公司比较好而且不贵除了阿里巴巴还有什么网站做外贸的

建站哪家公司比较好而且不贵,除了阿里巴巴还有什么网站做外贸的,重庆市住房和城乡建设厅网站,佛山建设网站公司哪家好8051汇编与C语言系列教程 作者将狼才鲸创建日期2024-07-23 CSDN文章地址#xff1a;【目录】8051汇编与C语言系列教程本Gitee仓库原始地址#xff1a;才鲸嵌入式/8051_c51_单片机从汇编到C_从Boot到应用实践教程 一、本教程目录 序号教程名称简述教程链接1点亮LCD灯通过IO…8051汇编与C语言系列教程 作者将狼才鲸创建日期2024-07-23 CSDN文章地址【目录】8051汇编与C语言系列教程本Gitee仓库原始地址才鲸嵌入式/8051_c51_单片机从汇编到C_从Boot到应用实践教程 一、本教程目录 序号教程名称简述教程链接1点亮LCD灯通过IO输出而点亮LCD灯教程链接2延时与函数用汇编准确延时函数C语言近似延时函数教程链接…………………… 二、参考网址 51单片机教程(简叙及目录)1~28章 51单片机教程(从原理开始基于汇编) 评分5分。51单片机汇编可以下载源码和工程能直接运行。教程非常入门有单片机介绍、组成部分、引脚介绍、Keil使用、汇编介绍、IO口介绍、寄存器、汇编指令介绍、数码管等。 单片机c语言教程1~17 评分5分。51单片机的C语言和32位ARM C语言有点不一样这个教程描述了8051专有的存储关键字Keil使用 零基础学习8051单片机一~十六 评分5分。和学校里学的类似讲解了8051单片机的结构、组成、有哪些历史厂商、常用寄存器的介绍。 51单片机入门教程0~6 评分4分。介绍了Keil和Proteus模拟器使用Proteus来运行程序展示了传统的流水灯、数码管、按键。 51单片机从零开始学习 评分4分。介绍了C语言关键字、Keil使用、流水灯、按键、数码管、模块化编程、定时器、串口、外设控制。 单片机学习教程(目录)1~20章 评分4分。51单片机C语言。 51单片机轻松入门—基于STC15W4K系列C语言版1-22章 评分4分。 51单片机入门基础教程(共十节) 评分4分。介绍了原理、引脚、代码。 单片机入门教程1~7 评分4分。51单片机介绍、键盘、LCD、定时器。 单片机教程 评分4分。代码不全。51单片机的一些项目设计万年历、宠物小屋、视频小车等 51 单片机1~18 评分4分。代码不全。一些单片机自带模块的项目。 51单片机-从零开始入门1~1451单片机教程1~6 评分3分。内容少。 单片机应用1~32屠龙刀STC32保姆级教程[共9篇-13个实验代码] 三、教程介绍 1、点亮一个LED灯 本源码包含C语言和汇编工程能直接在电脑中通过Keil模拟器运行并在Keil示波器窗口看到 IO 输出的矩形波。 源码及工程链接 汇编效果C语言效果 汇编源码 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; \brief 让LCD灯闪烁控制IO输出 ; \details 使用Keil Simulator模拟器和Debug时的Logic Analyzer示波器窗口查看输出 ; \remark File format: UTF-8源文件使用UTF-8中文编码 ; \note 省略了8051的初始化使用模拟器时会自动准备好运行环境 ; \author 将狼才鲸 ; \date 2024-07-18 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 头文件 ;; ;$INCLUDE(at89c51xd2.inc) ; 也可以使用 #include at89c51xd2.incKeil默认隐藏包含了8051通用寄存器不用重复包含MAIN: ; 标号或函数名; 请在Debug后通过 View--Analysis Windows--Logic Analyzer--Setup--点击右上角叉叉左边的新建图标--键入P1; --点击选中P1--在Max那里将0xFF改成0x01--在And Mask中将0xFFFFFFFF改成0x00000001--Close 来创建模拟器中示波器的采样引脚; 把示波器的小窗口向下拉一点露出波形的显示区域; 再点击Zoom中的All来显示整个波形不点击会看不到变化的波形; 再F10单步运行一边运行一边看输出的矩形波。CLR P1.0 ; P1_0 IO口置低P1.0里面的.0是汇编的特殊语法意思是前面P1寄存器的第0 bit能位寻址的寄存器都能这样调用SETB P1.0 ; P1_0 IO口拉高LJMP MAIN ; 跳转到标号END ; 源文件结束 C源码 /******************************************************************************* \brief 让LCD灯闪烁控制IO输出* \details 使用Keil Simulator模拟器和Debug时的Logic Analyzer示波器窗口查看输出* \remark File format: UTF-8源文件使用UTF-8中文编码* \note 省略了8051的初始化使用模拟器时会自动准备好运行环境* \author 将狼才鲸* \date 2024-07-18******************************************************************************//** 头文件 **/ #include Atmel/at89c51RC2.h /* 寄存器定义 *//** 接口函数 **/ int main(void) {while (1){/**请在Debug后通过 View--Analysis Windows--Logic Analyzer--Setup--点击右上角叉叉左边的新建图标--键入P1_0--Close 来创建模拟器中示波器的采样引脚把示波器的小窗口向下拉一点露出波形的显示区域再点击Zoom中的All来显示整个波形不点击会看不到变化的波形再F10单步运行一边运行一边看输出的矩形波。*/P1_0 0; /* P1_0是头文件中已经定义的寄存器Bit */P1_0 1;} } 参考网址 2课:单片机引脚介绍 该文章后半部分有C语言原始工程下载链接4课:第一个单片机小程序 该文章前半部分有汇编原始工程下载链接 2、延时与函数 ms、us级别的延时最好使用定时器ns级别的延时就可以关闭所有中断后使用汇编不需要精准的延时则可以使用C语言的for循环 使用C语言进行延时时延时的时间不好算一般通过实际测试得到并且延时的时间长短容易受到代码优化的影响在关闭中断的情况下汇编函数可以做到精准控制延时精度和指令周期一致前提是你要知道当前主频和每条指令的执行时间指令周期 延时可以用在IO输出的时序控制和通信端口模拟例如用IO口模拟I2C、SPI、SDIO等协议 源码及工程链接 该汇编工程里演示了汇编函数的编写、参数的调用和精准的延时该C语言工程里演示了一般的延时在C语言中如何调用汇编函数汇编效果C语言效果C语言中调用汇编精准延时效果 汇编精准延时函数源码 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; \brief 演示延时与函数控制LCD灯闪烁的间隔 ; \details 使用Keil Simulator模拟器和Debug时的Logic Analyzer示波器窗口查看输出 ; \remark File format: UTF-8源文件使用UTF-8中文编码 ; \note 省略了8051的初始化使用模拟器时会自动准备好运行环境 ; \author 将狼才鲸 ; \date 2024-07-21 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 8051汇编指令及每条指令所消耗的时钟数详见 ; https://gitee.com/langcai1943/8051-from-boot-to-application/blob/develop/02_doc/01_8051寄存器、指令集、伪指令和关键字介绍.md; 当前工程中配置的晶振是Keil默认的24MHz时钟周期41.67ns状态周期是2倍时钟周期即83.3333ns ; 机器周期是6倍状态周期即500ns一个机器周期记为1T一条指令周期为1~4T具体哪条指令占多少T需要查阅上面文档;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 声明 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; $NOMOD51 ; 不使用Keil默认隐含的8051通用寄存器定义而是显式的包含寄存器定义头文件;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 头文件 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; $INCLUDE(at89c51xd2.inc) ; 也可以使用 #include at89c51xd2.inc实际文件在C:\Keil_v5\C51\ASM\at89c51xd2.inc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 宏定义 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; INS_SET_10US_NUM EQU 20 ; 10us占20个机器周期当前工程中配置的晶振是Keil默认的24MHz时钟周期41.67ns机器周期500ns;; 宏定义函数 DELAY_1US MACRONOP ; 1T500nsNOP ENDMDELAY_2US MACRODELAY_1USDELAY_1US ENDMDELAY_4US MACRODELAY_2USDELAY_2US ENDMDELAY_8US MACRODELAY_4USDELAY_4US ENDMDELAY_16US MACRODELAY_8USDELAY_8US ENDM;; ; \brief 汇编主函数演示延时与函数控制LCD灯闪烁的间隔 ; 其实不算真正的函数只是标号 死循环 ; \param 无 ; \return 无 ;; MAIN: ; 标号或函数名; 请在Debug后通过 View--Analysis Windows--Logic Analyzer--Setup--点击右上角叉叉左边的新建图标--键入P1; --点击选中P1--在Max那里将0xFF改成0x01--在And Mask中将0xFFFFFFFF改成0x00000001--Close 来创建模拟器中示波器的采样引脚; 把示波器的小窗口向下拉一点露出波形的显示区域; 再点击Zoom中的All来显示整个波形不点击会看不到变化的波形; 再F10单步运行一边运行一边看输出的矩形波。CLR P1.0 ; 1T耗时1个机器周期P1_0 IO口置低P1.0里面的.0是汇编的特殊语法意思是前面P1寄存器的第0 bit能位寻址的寄存器都能这样调用;; 和CLR一起延时10us再补齐9.5usNOP ; 1T0.5usDELAY_8US ; 宏定义函数DELAY_1US;; 延时990usMOV R7, #99 ; 1T延时99 x 10 990us0xR7 十进制99调用函数时第一个参数是0xR7LCALL _delay_us_10x ; 2T传入的参数是R7;; 延时4msMOV R6, #HIGH(4) ; HIGH和LOW是Keil C51的伪指令用于获取16位立即数中的高字节和低字节MOV R7, #LOW(4) ; 传入的参数0xR6R7 4msLCALL _delay_ms ; 调用函数和C语言不一样汇编函数的定义即使在本调用下方的话不用先声明也能使用SETB P1.0 ; 1TP1_0 IO口拉高;; 和SETB、LJMP MAIN一起延时10us再补齐8.5usNOP ; 1T0.5usDELAY_8US ; 宏定义函数;; 延时1000usMOV R7, #99 ; 0xR7 99LCALL _delay_us_10x ; 传入的参数是99延时990us;; 延时9msMOV R6, #HIGH(9)MOV R7, #LOW(9) ; 传入的参数0xR6R7 9msLCALL _delay_msLJMP MAIN ; 2T跳转到标号;; ; \brief 以10us为单位的延时函数必须延时20us及以上否则请直接使用NOP进行延时 ; \details 1. 使用小写标号函数名在C语言中调用该函数时让函数名与C语言标准更统一 ; 2. 传入的参数需要 2否则会有异常的长时间延时也就是说至少延时2个10us ; \note 注意要使用汇编实现精准延时的话需要在调用延时前关闭所有中断调用后再恢复中断 ; \remark C语言调用此汇编函数时使用 delay_us_10x(100); // 延时1000us ; \param 0xR7 uint8延时多少个10us取值为 2 ~ 255 ; \return 无 ;; _delay_us_10x:; 外部调用此函数时如果有给R7赋值则会额外消耗1T已优化; 外部调用此函数的LCALL或ACALL会额外占2T已优化DEC R7 ; 1T将10us的次数直接减1用于补齐10us抵消额外的消耗;; 抵消额外消耗的6T再补14T凑成10us; \note 如果晶振有变化则这里的补时也要调整或者修改得和 INS_SET_10US_NUM 相关DELAY_4US ; 这里是宏定义函数DELAY_2USDELAY_1US;; 延时10us DELAY_US_LOOP2:MOV R6, #((INS_SET_10US_NUM - 4) / 2) ; 1TNOP ; 1T空指令什么也不做为了补齐MOV指令的1T时间到2T DELAY_US_LOOP1:DJNZ R6, DELAY_US_LOOP1 ; 2TDJNZ R7, DELAY_US_LOOP2 ; 2T延时多少个10usRET ; 从函数中返回返回额外占2T已优化;; ; \brief ms延时函数 ; \details 1. 传入的参数必须 1即至少延时1ms如果为0则会导致超长时间延时 ; 2. 当低字节为0x01时本延时函数少了2T也就是1us ; 3. 当低字节为0x00时本延时函数多了4T也就是2us ; \note 注意要使用汇编实现精准延时的话需要在调用延时前关闭所有中断调用后再恢复中断 ; \remark C语言调用此汇编函数时使用 delay_ms(1000); // 延时1000ms ; \param 0xR6R7--0xR4R5 uint16延时多少ms取值范围为0x0002~0xFEFF注意不是0xFFFF! ; \return 无 ;; _delay_ms:; 外部调用此函数时如果有给R6R7赋值则会额外消耗2T已优化; 外部调用此函数的LCALL或ACALL会额外占2T已优化; 0xR4R5 0xR6R7MOV A, R7 ; 1T因为之后R7还需作为子函数的参数所以将0xR6R7先移动到0xR4R5MOV R5, A ; 1T不能直接MOV R5, R7; 没有这种指令两个内部寄存器之间不能直接赋值MOV A, R6 ; 1T下面调用us延时函数时没有进行压栈弹栈操作us延时里面用了R6作为临时变量所以这里也要避开MOV R4, A ; 1TINC R4 ; 1TR4自增1如果R4本来为0则本函数最后的R4减1不为0跳转会有bug因此高位的取值只能是0x00~0xFE不能取到0xFF;; 函数进入和函数返回时的额外消耗是24T其中有2T是R4自增一导致的12us此处将其补齐到1ms; 还需额外补980us 8us并后续将0xR4R5减1也就是减去这补齐的1msDELAY_8USMOV R7, #98 ; 1T已抵消_delay_us_10x函数的参数980usLCALL _delay_us_10x ; 2T已抵消;; 将0xR4R5减1用于时间凑整补齐; 执行所有INC或DEC自增自减、所有ADD加法指令时都不会产生PSW寄存器的CY借位; 只有ADDC和SUBB指令才带进位或借位进位和借位都是PSW寄存器的CY位; SUBB不能用R0~R7去减别的数只能用累加器A去减;; 低字节减1MOV A, R5 ; 1TCLR C ; 1TCLR C和CLR CY是一样的效果C是指令集里面特定的用法CY是头文件中BIT伪指令定义的位SUBB A, #1 ; 1TMOV R5, A ; 1T减完后放回R5JNC DELAY_MS_HIGH_BYTE_IGNORE_DEC ; 2T如果CY进位为0则跳转;; 类似跳转指令还有JNZ累加器为1跳转JZ累加器为0跳转JNC进位为0跳转JC进位为1跳转; JNB比特为0跳转JB比特为1跳转JBC比特为1跳转并清零前面其他指令都不会自动清零;; 如果有借位则高字节减1因为有减1的步骤所以传入的参数不能为0x0001; 当低字节为0x00时此处会执行则会多了4T也就是2us; ;;CLR C ; 1TCLR C和CLR CY是一样的效果C是指令集里面特定的用法CY是头文件中BIT伪指令定义的位MOV A, R4 ; 1TSUBB A, #1 ; 1TSUBB使用前要清零进位CY否则借位存在的话会多减去1MOV R4, A ; 1T减完后放回R4DELAY_MS_HIGH_BYTE_IGNORE_DEC:;; 处理传入的参数低字节为0的情况MOV A, R5 ; 1TJZ DELAY_MS_HIGH_BYTE_JMP ; 2T累加器A为0则跳转LJMP DELAY_MS_BYTE_LOOP_LOW ; 2T当低字节减完1后为0时此处会少了2T也就是1us少的这1us没有进行优化; ;; DELAY_MS_BYTE_LOOP_HIGH:; 每次高字节循环256ms时的额外消耗是4T2us此处将其补齐到10us后面再调用990usDELAY_8US;; 延时1ms高字节减1低字节从256变成255时将少的那一次补上MOV R7, #99 ; 1T已抵消_delay_us_10x函数的参数LCALL _delay_us_10x ; 2T已抵消MOV R5, #0FFH ; 2TR5 255;; 延时1 ~ 255ms DELAY_MS_BYTE_LOOP_LOW:; 每次低字节循环1ms时的额外消耗是2T1us此处将其补齐到10us后面再调用990usDELAY_8USDELAY_1US;; 延时1msMOV R7, #99 ; 1T已抵消_delay_us_10x函数的参数LCALL _delay_us_10x ; 2T已抵消DJNZ R5, DELAY_MS_BYTE_LOOP_LOW ; 2TR5寄存器内的数据减1不为0则跳转延时多少个msDELAY_MS_HIGH_BYTE_JMP:DJNZ R4, DELAY_MS_BYTE_LOOP_HIGH ; 2T处理高字节延时多少个256ms之前有加1函数调用时这里额外多了2T已优化RET ; 2T返回额外占2T已优化END ; 源文件结束 C语言不精准的延时函数源码 /******************************************************************************* \brief 演示延时与函数控制LCD灯闪烁的间隔* \details 使用Keil Simulator模拟器和Debug时的Logic Analyzer示波器窗口查看输出* \remark File format: UTF-8源文件使用UTF-8中文编码* \note 省略了8051的初始化使用模拟器时会自动准备好运行环境* \author 将狼才鲸* \date 2024-07-23******************************************************************************//* 一个文件中将一些全局的东西分门别类模块性更好不容易错漏方便查找bug *//********************************* 头文件 *************************************/ #include Atmel/at89c51RC2.h /* 寄存器定义 *//******************************** 类型定义 ************************************/ /** 下面的类型定义让程序的可移植性更好例如无负担的移植到32位或64位MCU上typedef是C语言进行类型定义的关键字很常用 */ typedef unsigned char uint8; /* 使用uint8代替8051一个字节的unsigned char */ typedef unsigned int uint16; /* 使用uint16代替8051两个字节的unsigned int8051的int是2字节而32位CPU是4字节 */ typedef unsigned long int uint32; /* 8051的long是4字节而32位CPU是8字节4字节的float和8字节的double与32位CPU一致 */ typedef char int8; typedef int int16; typedef long int int32;/********************************* 宏定义 *************************************/ /** 定义错误码正式工程建议所有的返回值都用错误码不要直接返回 -1这样模块化更好 */ #define OK 0 #define ERR (-1) /* 宏定义如果是表达式建议用括号括起来防止优先级错误导致难定位的bug出现 */ #define ERR_PARAM (-2) /* 传入的参数错误 *//****************************** 结构体定义*************************************/ typedef enum _BOOL { /* enum是C语言的关键词联合体很常用 */FALSE 0,TRUE /* enum的项目如果是缺省值则值默认是上一个值 1 */ } BOOL; /* C语言标准库没有定义布尔类型所以自己定义当然Linux和Windows的一些库文件中会有 *//******************************* 函数声明 *************************************/ static int16 sleep_us_10x(int16 cnt); /* 函数声明如果有函数在定义前就被调用则需要在调用前进行声明 */ static int16 sleep_ms(int16 cnt); /* static是C语言关键字表示静态变量或静态函数这里是静态函数 *//******************************* 接口函数 *************************************/ /*** \brief 主函数* \details 当整个工程文件中没有汇编文件时Debug后默认进入到main函数第一行* 但当有汇编文件时默认会先进入到汇编此时则需要写好boot*/ int main(void) {/* 进入函数时先拉低拉高做标记作为延时的起始零点因为main执行前会有耗时 */P1_0 0; /* IO口输出低P1_0是头文件中已经定义的寄存器Bit */P1_0 1; /* IO口输出高 */while (1){/**请在Debug后通过 View--Analysis Windows--Logic Analyzer--Setup--点击右上角叉叉左边的新建图标--键入P1_0--Close 来创建模拟器中示波器的采样引脚把示波器的小窗口向下拉一点露出波形的显示区域再点击Zoom中的All来显示整个波形不点击会看不到变化的波形再F10单步运行一边运行一边看输出的矩形波。*/P1_0 0; /* IO口输出低P1_0是头文件中已经定义的寄存器Bit */sleep_us_10x(500); /* 延时5ms */P1_0 1; /* IO口输出高 */sleep_ms(15); /* 延时15ms */} }/******************************* 私有函数 *************************************/ /*** \brief us级别延时延时时间 cnt x 10 us* \details 没使用定时器只使用了死循环当前延时只是大体上准而且会受中断的影响* 只延时10us时误差13.5us* 延时20us及以上时误差 0.25% x 想要的延时 13.5us例如想要延时1ms实际会延时1016us* \param cnt 延时多少个10us有效取值范围为 1 ~ 32768* \return 返回值无实际含义*/ static int16 sleep_us_10x(int16 cnt) /* 不是供其它模块调用的接口函数都建议定义成静态函数模块化更好 */ {int16 i; /* 8051的所有局部变量必须在函数的前面定义不能像32位CPU那样随用随定义 */int8 ret 0;/* cnt为0时延时12uscnt为1时延时19.5us通过实测得到 *//* 循环1001次耗时8021us每次循环消耗8.0015us通过实测得到 */for (i 0; i cnt; i) /* 8051不能使用 for (int i 0; i cnt; i); 这种使用时定义的做法 */{/** 每次空循环耗时8us此处再补2us补成10us *//* 也可以使用 __asm NOP; 插入汇编但是这样会导致.c文件打不了断点所以不用 *//* 指令集参见https://gitee.com/langcai1943/8051-from-boot-to-application/blob/develop/02_doc/01_8051寄存器、指令集、伪指令和关键字介绍.md */ret; /* 字节自增在汇编中消耗1个机器周期当前24MHz晶振一个机器周期0.5us */ret;ret;ret;}return ret; /* 使用ret防止此变量的所有操作被编译器优化掉该返回值没有含义 */ }/*** \brief ms级别延时* \details 没使用定时器只使用了死循环当前延时只是大体上准而且会受中断的影响* 延时误差约 0.1%例如延时1000ms实际耗时约1001ms* \param cnt 延时多少个ms有效取值范围为 1 ~ 32768* \return 返回值无实际含义*/ static int16 sleep_ms(int16 cnt) {int16 i, j; # define MS_SLEEP_CNT 356 /* 延时1ms所需要的循环次数实测所得 */for (i 0; i cnt; i){for (j 0; j MS_SLEEP_CNT; j){}}return 0; /* 返回0代表一切执行正常处理错误则返回负数的错误码建议所有函数都弄成有返回值的结构中断处理除外 */ }/********************************* 文件尾 *************************************/ C语言中调用精准汇编延时函数的源码 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ; \brief 汇编延时函数供C语言调用 ; \remark File format: UTF-8源文件使用UTF-8中文编码 ; \author 将狼才鲸 ; \date 2024-07-22 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;$NOMOD51 ; 不使用Keil默认隐含的8051通用寄存器定义而是显式的包含寄存器定义头文件;; 定义本汇编文件的代码段不定义的话放到位置不固定有可能上电RESET后直接就跑到这里来了 ; ; 如果该汇编文件会被别的汇编文件直接包含则不用定义同时文件尾也不用加END NAME DELAY_ASM DELAY_ASM SEGMENT CODE ; SEGMENT CODE是定义代码段 RSEG DELAY_ASM; 8051汇编指令及每条指令所消耗的时钟数详见 ; https://gitee.com/langcai1943/8051-from-boot-to-application/blob/develop/02_doc/01_8051寄存器、指令集、伪指令和关键字介绍.md; 当前工程中配置的晶振是Keil默认的24MHz时钟周期41.67ns状态周期是2倍时钟周期即83.3333ns ; 机器周期是6倍状态周期即500ns一个机器周期记为1T一条指令周期为1~4T具体哪条指令占多少T需要查阅上面文档;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 函数声明 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; PUBLIC _delay_us_10x ; 类似于C语言头文件中的 extern void delay_us_10x(uint8 cnt); 让别的文件中能调用此函数 PUBLIC _delay_ms;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 宏定义 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; INS_SET_10US_NUM EQU 20 ; 10us占20个机器周期当前工程中配置的晶振是Keil默认的24MHz时钟周期41.67ns机器周期500ns;; 宏定义函数 DELAY_1US MACRONOP ; 1T500nsNOP ENDMDELAY_2US MACRODELAY_1USDELAY_1US ENDMDELAY_4US MACRODELAY_2USDELAY_2US ENDMDELAY_8US MACRODELAY_4USDELAY_4US ENDMDELAY_16US MACRODELAY_8USDELAY_8US ENDM;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; 接口函数 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; ; \brief 以10us为单位的延时函数必须延时20us及以上否则请直接使用NOP进行延时 ; \details 1. 使用小写标号函数名在C语言中调用该函数时让函数名与C语言标准更统一 ; 2. 传入的参数需要 2否则会有异常的长时间延时也就是说至少延时2个10us ; \note 注意要使用汇编实现精准延时的话需要在调用延时前关闭所有中断调用后再恢复中断 ; \remark C语言调用此汇编函数时使用 delay_us_10x(100); // 延时1000us ; \param 0xR7 uint8延时多少个10us取值为 2 ~ 255 ; \return 无 ;; _delay_us_10x:; 外部调用此函数时如果有给R7赋值则会额外消耗1T已优化; 外部调用此函数的LCALL或ACALL会额外占2T已优化DEC R7 ; 1T将10us的次数直接减1用于补齐10us抵消额外的消耗;; 抵消额外消耗的6T再补14T凑成10us; \note 如果晶振有变化则这里的补时也要调整或者修改得和 INS_SET_10US_NUM 相关DELAY_4US ; 这里是宏定义函数DELAY_2USDELAY_1US;; 延时10us DELAY_US_LOOP2:MOV R6, #((INS_SET_10US_NUM - 4) / 2) ; 1TNOP ; 1T空指令什么也不做为了补齐MOV指令的1T时间到2T DELAY_US_LOOP1:DJNZ R6, DELAY_US_LOOP1 ; 2TDJNZ R7, DELAY_US_LOOP2 ; 2T延时多少个10usRET ; 从函数中返回返回额外占2T已优化;; ; \brief ms延时函数 ; \details 1. 传入的参数必须 1即至少延时1ms如果为0则会导致超长时间延时 ; 2. 当低字节为0x01时本延时函数少了2T也就是1us ; 3. 当低字节为0x00时本延时函数多了4T也就是2us ; \note 注意要使用汇编实现精准延时的话需要在调用延时前关闭所有中断调用后再恢复中断 ; \remark C语言调用此汇编函数时使用 delay_ms(1000); // 延时1000ms ; \param 0xR6R7--0xR4R5 uint16延时多少ms取值范围为0x0002~0xFEFF注意不是0xFFFF! ; \return 无 ;; _delay_ms:; 外部调用此函数时如果有给R6R7赋值则会额外消耗2T已优化; 外部调用此函数的LCALL或ACALL会额外占2T已优化; 0xR4R5 0xR6R7MOV A, R7 ; 1T因为之后R7还需作为子函数的参数所以将0xR6R7先移动到0xR4R5MOV R5, A ; 1T不能直接MOV R5, R7; 没有这种指令两个内部寄存器之间不能直接赋值MOV A, R6 ; 1T下面调用us延时函数时没有进行压栈弹栈操作us延时里面用了R6作为临时变量所以这里也要避开MOV R4, A ; 1TINC R4 ; 1TR4自增1如果R4本来为0则本函数最后的R4减1不为0跳转会有bug因此高位的取值只能是0x00~0xFE不能取到0xFF;; 函数进入和函数返回时的额外消耗是24T其中有2T是R4自增一导致的12us此处将其补齐到1ms; 还需额外补980us 8us并后续将0xR4R5减1也就是减去这补齐的1msDELAY_8USMOV R7, #98 ; 1T已抵消_delay_us_10x函数的参数980usLCALL _delay_us_10x ; 2T已抵消;; 将0xR4R5减1用于时间凑整补齐; 执行所有INC或DEC自增自减、所有ADD加法指令时都不会产生PSW寄存器的CY借位; 只有ADDC和SUBB指令才带进位或借位进位和借位都是PSW寄存器的CY位; SUBB不能用R0~R7去减别的数只能用累加器A去减;; 低字节减1MOV A, R5 ; 1TCLR C ; 1TCLR C和CLR CY是一样的效果C是指令集里面特定的用法CY是头文件中BIT伪指令定义的位SUBB A, #1 ; 1TMOV R5, A ; 1T减完后放回R5JNC DELAY_MS_HIGH_BYTE_IGNORE_DEC ; 2T如果CY进位为0则跳转;; 类似跳转指令还有JNZ累加器为1跳转JZ累加器为0跳转JNC进位为0跳转JC进位为1跳转; JNB比特为0跳转JB比特为1跳转JBC比特为1跳转并清零前面其他指令都不会自动清零;; 如果有借位则高字节减1因为有减1的步骤所以传入的参数不能为0x0001; 当低字节为0x00时此处会执行则会多了4T也就是2us; ;;CLR C ; 1TCLR C和CLR CY是一样的效果C是指令集里面特定的用法CY是头文件中BIT伪指令定义的位MOV A, R4 ; 1TSUBB A, #1 ; 1TSUBB使用前要清零进位CY否则借位存在的话会多减去1MOV R4, A ; 1T减完后放回R4DELAY_MS_HIGH_BYTE_IGNORE_DEC:;; 处理传入的参数低字节为0的情况MOV A, R5 ; 1TJZ DELAY_MS_HIGH_BYTE_JMP ; 2T累加器A为0则跳转LJMP DELAY_MS_BYTE_LOOP_LOW ; 2T当低字节减完1后为0时此处会少了2T也就是1us少的这1us没有进行优化; ;; DELAY_MS_BYTE_LOOP_HIGH:; 每次高字节循环256ms时的额外消耗是4T2us此处将其补齐到10us后面再调用990usDELAY_8US;; 延时1ms高字节减1低字节从256变成255时将少的那一次补上MOV R7, #99 ; 1T已抵消_delay_us_10x函数的参数LCALL _delay_us_10x ; 2T已抵消MOV R5, #0FFH ; 2TR5 255;; 延时1 ~ 255ms DELAY_MS_BYTE_LOOP_LOW:; 每次低字节循环1ms时的额外消耗是2T1us此处将其补齐到10us后面再调用990usDELAY_8USDELAY_1US;; 延时1msMOV R7, #99 ; 1T已抵消_delay_us_10x函数的参数LCALL _delay_us_10x ; 2T已抵消DJNZ R5, DELAY_MS_BYTE_LOOP_LOW ; 2TR5寄存器内的数据减1不为0则跳转延时多少个msDELAY_MS_HIGH_BYTE_JMP:DJNZ R4, DELAY_MS_BYTE_LOOP_HIGH ; 2T处理高字节延时多少个256ms之前有加1函数调用时这里额外多了2T已优化RET ; 2T返回额外占2T已优化END ; 源文件结束如果该汇编文件会被别的汇编文件直接包含则不用加END /******************************************************************************* \brief 演示在C语言中调用汇编函数和汇编宏定义函数* \details 使用Keil Simulator模拟器和Debug时的Logic Analyzer示波器窗口查看输出* \remark File format: UTF-8源文件使用UTF-8中文编码* \note 工程里包含了汇编文件的话还需要自己手写Boot代码或者创建工程时添加Keil默认的Boot代码* \author 将狼才鲸* \date 2024-07-23******************************************************************************//********************************* 头文件 *************************************/ #include Atmel/at89c51RC2.h/******************************** 类型定义 ************************************/ typedef unsigned char uint8; typedef unsigned int uint16; typedef unsigned long int uint32; typedef char int8; typedef int int16; typedef long int int32;/********************************* 宏定义 *************************************/ /** 错误码 */ #define OK 0 #define ERR (-1) /* 通用的错误码 */ #define ERR_PARAM (-2) /* 传入的参数错误 *//****************************** 结构体定义*************************************/ typedef enum _BOOL {FALSE 0,TRUE /* TRUE 1 */ } BOOL;/******************************* 函数声明 *************************************/ extern void delay_us_10x(uint8 cnt); extern void delay_ms(uint16 cnt);/* 如果是在一个汇编文件中调用另一个汇编文件里的函数采用 EXTRN CODE (YOUR_FUNCTION_NAME) 的方式进行声明 *//******************************* 接口函数 *************************************/ /*** \brief 主函数* \details 当整个工程文件中没有汇编文件时Debug后默认进入到main函数第一行* 但当有汇编文件时默认会先进入到汇编此时则需要写好boot*/ int main(void) {P1_0 0;P1_0 1;while (1){/**请在Debug后通过 View--Analysis Windows--Logic Analyzer--Setup--点击右上角叉叉左边的新建图标--键入P1_0--Close 来创建模拟器中示波器的采样引脚把示波器的小窗口向下拉一点露出波形的显示区域再点击Zoom中的All来显示整个波形不点击会看不到变化的波形再F10单步运行一边运行一边看输出的矩形波。*/P1_0 0; /* IO口输出低P1_0是头文件中已经定义的寄存器Bit */delay_us_10x(200); /* 调用汇编函数延时2000us */P1_0 1; /* IO口输出高 */delay_ms(8); /* 调用汇编函数延时8ms */} }/********************************* 文件尾 *************************************/ 参考网址 4课:第一个单片机小程序 该文章前半部分有汇编原始工程下载链接C51中汇编的使用及参数传递与数据返回试把如下c函数改写成汇编语言函数,用51汇编完整写一个函数当AT89C51单片机外接晶振为6MHz时其震荡周期、状态时钟周期、机器周期、指令周期的值各是多少Keil官方8051 Instruction Set Manual指令集在线查看MicroChip Atmel官方指令集文档8051 Microcontroller Instruction Set下载MicroChip Atmel官方芯片手册Atmell 8051 Microcontrollers Hardware Manual下载[Keil官方MCS-51 INSTRUCTION SET指令集文档下载]((https://www.keil.com/dd/docs/datashts/intel/ism51.pdf) Keil C51 C语言中调用汇编函数时最多使用三个参数默认第一个参数从R7开始放第二个参数从R5开始放第三个参数从R3开始放如果是2字节的int那么遵循8051的大端模式高字节放在R2、R4、R6低字节放在R3、R5、R7如果是指针参数无论是第一第二第三个参数都放在R1~R3存储类型是R3指针值是0xR2R1注意此时是小端模式存储超过三个的参数请用外部RAM来实现 汇编函数将返回值返回给C语言时返回值需要放在R7开始的位置char就放在R7int是0xR6R7long和float是0xR4R5R6R7指针是类型在R3指针值0xR2R1 汇编里的函数如果要给C语言用那么函数名标号前要叫下划线例如 _LOOP: NOP; RET;C语言调用时去掉下划线如LOOP(); 其实加下划线是代表有参数调用但是无参数的汇编函数你也这么加并没有问题 想找原文的在Keil安装目录的C:\Keil_v5\C51\Hlp\c51.chm在里面搜索Parameter Passing或者Passing in Registers原文摘抄如下 Passing in Registers C functions may pass parameters in registers and fixed memory locations. A maximum of 3 parameters may be passed in registers. All other parameters are passed using fixed memory locations. The following tables define which registers are used for passing parameters. Arg Numberchar, 1-byte ptrint, 2-byte ptrlong, floatgeneric ptr1R7R6 R7 (MSB in R6,LSB in R7)R4—R7R1—R3 (Mem type in R3, MSB in R2, LSB in R1)2R5R4 R5 (MSB in R4,LSB in R5)R4—R7R1—R3 (Mem type in R3, MSB in R2, LSB in R1)3R3R2 R3 (MSB in R2,LSB in R3)R1—R3 (Mem type in R3, MSB in R2, LSB in R1) The following examples clarify how registers are selected for parameter passing. DeclarationDescriptionfunc1 ( int a)The first and only argument, a, is passed in registers R6 and R7.func2 ( int b, int c, int *d)The first argument, b, is passed in registers R6 and R7. The second argument, c, is passed in registers R4 and R5. The third argument, d, is passed in registers R1, R2, and R3.func3 ( long e, long f)The first argument, e, is passed in registers R4, R5, R6, and R7. The second argument, f, cannot be located in registers since those available for a second parameter with a type of long are already used by the first argument. This parameter is passed using fixed memory locations.func4 ( float g, char h)The first argument, g, passed in registers R4, R5, R6, and R7. The second parameter, h, cannot be passed in registers and is passed in fixed memory locations. Copyright © Keil, An ARM Company. All rights reserved. * 、中断 * 参考网址   * 19课:单片机定时器、中断试验 5、定时器计数器参考网址 20课:单片机定时/计数器实验 串口收发 22课:单片机串行口通信程序设计IO波形模拟数码管 3课:LED数码管静态显示接口与编程按钮IO输入
http://www.w-s-a.com/news/540437/

相关文章:

  • 建设vip网站相关视频网站营销建设公司
  • 微站直播平台杭州seo按天计费
  • seo 新旧网站 两个域名福州设计网站建设
  • 如何做网站客户端如何做网络营销网站
  • 苏州网站建设制度打鱼网站建设
  • 瓜子二手车直卖网上海小红书seo
  • 天津中小企业网站制作珠海做网站的
  • 网站排名影响因素最牛的科技网站建设
  • 长春网站建设公司怎么样电商网站建设与开发期末考试
  • 品牌网站建设搭建国内外网站建设
  • 辽宁人社app一直更新整站seo定制
  • 兰州网站建设论坛装修品牌
  • 云南省城乡住房与建设厅网站用什么网站可以做电子书
  • 自己电脑怎么做网站服务器吗0基础如何做网站
  • 做网站的股哥网络整合营销方案策划
  • 网站你懂我意思正能量晚上唯品会网站开发费用
  • 网站认证金额怎么做分录网页无法访问是怎么回事
  • 樟木头建网站的wordpress自适应吸附菜单
  • 番禺网站设计威海微网站建设
  • 新乡网站建设服务网站建设的点子
  • 赛罕区城乡建设局网站什么是新媒体运营
  • 松原企业网站建设设计素材网排名
  • 网站建设是那个行业广东公司排名
  • 制作网站要多少钱seo是如何优化
  • 求个网站2020急急急做金融网站拘留多久
  • 网站后台管理系统怎么进seo网络推广外包公司
  • 中山市 做网站网站建设如何上传文件
  • 网站呢建设公众号制作要求
  • 网站备案证明在自己电脑上做网站
  • 沈阳旅游团购网站建设怎么制作网站搜索窗口