• [技术干货] 指令集与单片机的小故事
    工作原理设计       如果要去造一台计算机的话,那计算机所要具备的最重要的功能就是计算了。所以,在硬件开发当中,开发者会通过对于硬件电路的开发设计来实现计算的相关功能。寄存器,将数据或想要运行的指令先存储起来,以此来实现记忆以及循环操作的功能。当电路拥有了一些不同的部分之后,就已经构成一个完整的计算机电路了。并且现代所有的计算机都是基于这个原则来进行设计的,只不过是在于电路的复杂程度不一样而已。当开发者想要去实现不同功能的时候,其实就相当于实现了某一个程序。程序的运行过程其实是通过一系列最简单的指令集合,通过一个个步骤来完成对于计算机数据的处理,所以另一个在单片机中最重要概念就是指令集。 指令集      在计算机中,指示计算机硬件执行某种运算、处理功能的命令称为指令。指令是计算机运行的最小的功能单位,而硬件的作用是完成每条指令规定的功能。一台计算机上全部指令的集合,就是这台计算机的指令系统。指令系统也称指令集,是这台计算机全部功能的体现。而人们设计计算机首要考虑的是它拥有的功能,也就是首先要按功能档次设计指令集,然后按指令集的要求在硬件上实现。指令系统不仅仅是指令的集合,还包括全部指令的指令格式、寻址方式和数据形式。      指令集体系结构(Instruction Set Architecture,ISA),是软件和硬件之间接口的一个完整定义。ISA定义了一台计算机可以执行的所有指令的集合,每条指令规定了计算机执行什么操作,所处理的操作数存放的地址空间以及操作数类型。      ISA规定的内容包括数据类型及格式,指令格式,寻址方式和可访问地址空间的大小,程序可访问的寄存器个数、位数和编号,控制寄存器的定义,/o空间的编制方式,中断结构,机器工作状态的定义和切换,输入输出结构和数据传送方式,存储保护方式等。      计算机组成是指计算机主要功能部件的组成结构、逻辑设计及功能部件间的相互连接关系。计算机系统结构的经典定义是指程序设计者(主要指低级语程序员或编译程序设计者)所看到的计算机系统的属性,即计算机的功能特性和概念性结构,也称指令体系结构指令集体系结构(Instruction Set Architecture, ISA),是软件和硬件之间接口的一个完整定义。ISA定义了一台计算机可以执行的所有指令的集合,每条指令规定了计算机执行什么操作,所处理的操作数存放的地址空间以及操作数类型。ISA规定的内容包括数据类型及格式,指令格式,寻址方式和可访问地址空间的大小,程序可访问的寄存器个数、位数和编号,控制寄存器的定义,I/O空间的编制方式,中断结构,机器工作状态的定义和切换,输入输出结构和数据传送方式,存储保护方式等。因此,可以看出,指令集体系结构是指软件能够感知到的部分,也称软件可见部分。ISA处于硬件和软件的交界面上,硬件所有的功能都有ISA集中体现,软件通过ISA在计算机上执行。所以,ISA是整个计算机系统中的核心部分。通常指令集分为两种,复杂指令集和精简指令集      1、CISC(复杂指令集):指令集较丰富,对特殊任务有专用的特殊指令,执行特殊功能(科学计算之类的)也就是说指令集里面的每个指令较长,每个指令都分成好几个微指令来处理,正是因为指令集丰富所以在开发程序时较简单,但在执行时效率较低,处理数据时速度较慢,使得译 码器翻译二进制代码时较慢。      2、RISC(精简指令集计算机):注重的是指令集的优化,RISC的攻计看对哪吊用的指令进行优化使它们更加简单高效,对于那些不常用的指令会将具组合起来去买现某些特殊的功能,所以RISC指令集位数较短,所以执行效率高,译码和处理数据较快。      RISC 的设计初衷针对 CISC CPU 复杂的弊端,选择一些可以在单个 CPU 周期完成的指令,以降低 CPU 的复杂度,将复杂性交给编译器。举一个例子,CISC 提供的乘法指令,调用时可完成内存 a 和内存 b 中的两个数相乘,结果存入内存 a,需要多个 CPU 周期才可以完成;而RISC 不提供“一站式”的乘法指令,需调用四条单 CPU 周期指令完成两数相乘:内存 a 加载到寄存器,内存 b 加载到寄存器,两个寄存器中数相乘,寄存器结果存入内存 a。按照此思路,早期的设计出的 RISC 指令集,指令数是比 CISC 少些,但后来,很多 RISC 的指令集中指令数反超了 CISC,因此,引用指令的复杂度而非数量来区分两种指令集。      当然,CISC 也是要通过操作内存、寄存器、运算器来完成复杂指令的。它在实现时,是将复杂指令转换成了一个微程序,微程序在制造 CPU 时就已存储于微服务存储器。一个微程序包含若干条微指令(也称微码),执行复杂指令时,实际上是在执行一个微程序。这也带来两种指令集的一个差别,微程序的执行是不可被打断的,而 RISC 指令之间可以被打断,所以理论上 RISC 可更快响应中断。      所以,RISC 的优点就在于在使用相同的晶片技术和相同运行时钟下,RISC 系统的运行速度将是 CISC 的 2~4 倍。由于 RISC 处理器的指令集是精简的,它的记忆体管单元、浮点单元等都能设计在同一块晶片上。RISC 处理器比相对应的 CISC 处理器设计更简单,所需要的时间将变得更短,并可以比 CISC 处理器应用更多先进的技术,开发更快的下一代处理器。处理单元      设计的计算机有了一定的工作原理和指令集之后,就需要对负责计算的集成电路,处理单元有一定的了解。现代可编程设备可以被分为微处理器(Microprocessor, MPU)和微控制器(Microcontroller, MCU)。其中微处理器通常就是 CPU,它常被用来处理大规模,复杂程度非常高的计算。微处理器的特点就在于它只能用来处理数据,其他必要的存储器等部件必须外接才能使用。从家里老电脑拆了几个下来:微处理器(Microprocessor, MPU)微控制器(Microcontroller, MCU)      与微处理器相比,微控制器就是一个非常完整的设备,也就是上文提到过的单片机。它其中包含了 CPU、存储器和一系列输入输出的设备。由于微控制器当中 CPU 的计算能力相比于MPU 一般来说要弱很多,所以它一般设计为一些低功耗的嵌入式设备所使用。除此之外,微控制器和微处理器之间另一个比较大的区别就在于 CPU 的时钟频率不同。由于MPU 通常被设计用于高复杂度的任务计算,所以它的时钟频率相对来讲是比较高的。而MCU 通常被用在一些低功耗设备或一些无复杂计算能力的物联网设备上,为了保证使用时长,所以 MCU 当中的 CPU 通常时钟频率较低。单片机架构      单片机的架构总共可以分为四个不同的部分:CPU、存储器、输入输出接口和系统总线。本小节将对这些不同的部分逐个进行介绍,首先是 CPU,CPU 主要是用来负责数据的处理用于计算,它在单片机当中用来控制整一个系统,它串行地读取并解码程序指令,执行需要处理器的任务,并且为其余部分任务生成控制信号。它执行所有算术和逻辑运算,并且具有相同处理器的微控制器可以执行相同的程序。      存储系统当中所包含的就是存储器和地址解码电路,其中存储器会被分为两个不同的类别。其中之一是随机存储器,又叫 RAM(Random Access Memory),它用来存储运行在程序当中的数据等等,它的特点就在于断电之后所有的数据都会消失。除了 RAM 之外的另一个叫做只读存储器,又叫 ROM(Read Only Memory),它用来存储需要被单片机执行的程序,并且它的特点就在于断电之后数据不会消失,它会被一直保存在存储器当中。ROM 和 RAM 的区别就在于 RAM 类似于电脑的内存,本身不保存数据,但是日常使用必然会占用它的空间缓存数据。而 ROM 就是电脑的硬盘,它被用来存放大量的数据,并且电脑关机后,硬盘当中的数据也不会消失。      输入输出接口就是用来和外部设备相连接的接口,比方说数字 IO,模拟 IO 等等,开发者可以使用数据线或其他设备对其与外部进行连接。       总线的作用如同电路当中的导线,它可以将各个部分都连接起来并进行数据的通信。通常情况下,总线可以被分为三类:数据总线、地址总线和控制总线。数据总线顾名思义,就是用于在 CPU、存储器和外界设备之间传递数据的。地址总线是选择处理器需要读取或写入到特定存储器位置的一条总线,并且它的特点就在于地址总线的数据流动方向是单向的,是从 CPU 到地址总线,再从地址总线到存储器。      最后一个控制总线主要就是用于控制或信令数据的传输。
  • [技术干货] 简易加法计算器
          用程序实现从板子上标有0~9 数字的按键输入相应数字,该数字要实时显示到数码管上,用标有向上箭头的按键代替加号,按下加号后可以再输入一串数字,然后回车键计算加法结果,并同时显示到数码管上。      把抽象的行列数转换为了一种叫做标准键盘键码(就是电脑键盘的编码)的数据,然后用得到的这个数据作为下一步分支判断执行动作的依据,有助于程序的层次化而方便维护与移植#include <reg52.h> sbit ADDR0 = P1^0; sbit ADDR1 = P1^1; sbit ADDR2 = P1^2; sbit ADDR3 = P1^3; sbit ENLED = P1^4; sbit KEY_IN_1 =P2^4; sbit KEY_IN_2= P2^5; sbit KEY_IN_3 = P2^6; sbit KEY_IN_4= P2^7; sbit KEY_OUT_1 = P2^3; sbit KEY_OUT_2 = P2^2; sbit KEY_OUT_3 = P2^1; sbit KEY_OUT_4= P2^0; unsigned char code LedChar[ ] = {//数码管显示字符转换表 0xc0,0xF9,0xA4,0xB0,ox99,0x92,0x82,OxF8,0x80,0x90,0x88,0x83,0xc6,0xA1,0×86,0x8E} ; unsigned char LedBuff[6]= { //数码管显示缓冲区 0xFF,OxFE,OxFF,OxFF,OxFF,0xFF }; unsigned char code KeyCodeMap[4][4] = { //矩阵按键编号到标准键盘键码的映射表 { 0x31,0x32,0x33,0x26 },//数字键1、数字键2、数字键3、向上键 {0x34,0x35,0x36,0x25 },//数字键4、数字键5、数字键6、向左键 {0x37,0x38,0x39,0x28 },//数字键7、数字键8、数字键9、向下键 {0x30,0x1B,0x0D,0x27 }//数字键0、EsC键、回车键、向右键 }; unsigned char KeySta[ 4][4] = {//全部矩阵按键的当前状态 {1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1} } ; void KeyDriver(); void main() { EA = 1; //使能总中断 ENLED - 0; //选择数码管讲行显示 ADDR3 = 1; TMOD = 0x01; //设置T0为模式1 THO= OxFC; //为T0赋初值OxFC67,定时1msTLO= 0x67; ETO = l; //使能T0中断 TRO= 1; //启动T0 LedBuff[0]= LedChar [0] ; //上电显示0 while (1) { KeyDriver(); //调用按键驱动函数 } } /* 将一个无符号长整型的数字显示到数码管上,num-待显示数字 */ void ShowNumber(unsigned long num) { signed char i; unsigned char buf [6]; for (i=0 ; i<6; i++) //把长整型数转换为6位十进制的数组 buf [i] = num % 10; num = num / 10; } for (i=5; i>=l; i--) //从最高位起,遇到ü转换为空格,遇到非o则退出循环 if(buf[i]== 0) LedBuff[i]=OxFF;else break; } for ( ; i>=0 ; i--) //剩余低位都如实转换为数码管显示字符 LedBuff [i] = LedChar [buf[i] ]; } } /* 按键动作函数,根据键码执行相应的操作,keycode-按键键码 */ void KeyAction (unsigncd char keycodc) { static unsigned long result = 0; //用于保存运算结果 static unsigned long addend = 0; //用于保存输入的加数 if ((keycode>=0x30) &&(keycode<=Ox39)) //输入0-9的数字 addend = (addend*10)+( keycode-0x30); //整体十进制左移,新数字进入个位 ShowNumber(addend) ; //运算结果显示到数码管 else if (keycode == 0x26) //向上键用作加号,执行加法或连加运算 result +=addend; //进行加法运算 addend = 0 ; ShowNumber (result); //运算结果显示到数码管 } else if (keycode == 0xOD) //回车键,执行加法运算(实际效果与加号相同) result += addend; //进行加法运算 addend = 0 ; ShowNumber (result); //运算结果显示到数码管 else if(keycode == 0x1B) //Esc键,清零结果 { addend = 0 ;result = 0 ; showNumber(addend); //清零后的加数显示到数码管 } } /*按键驱动函数,检测按键动作,调度相应动作函数,需在主循环中调用*/ void KeyDriver() { unsigned char i,j; static unsigned char backup[4][4] = { //按键值备份,保存前一次的值 { 1,1,1,1},{1,1,1,1},{1,1,1,1},{1,1,1,1}}; for (i=0; i<4;i++) //循环检测4*4的矩阵按键 for (j=0; j<4; j++) { if(backup [i][j] != KeySta[i][j]) //检测按键动作 if(backup[i][j] != 0) //按键按下时执行动作 { KeyAction (KeyCodeMap [i][j]); //调用按键动作函数 } backup [i][j]=Keysta[i][j]; //刷新前一次的备份值 } } } /*按键扫描函数,需在定时中断中调用,推荐调用间隔1ms */ void keyscan () { unsigned char i; static unsigned char keyout = 0; //矩阵按键扫描输出索引 static unsigned char keybuf[4] [4] = { //矩阵按键扫描缓冲区 { OxFF,OxFF,OxFF,OxFF},{OxFF,OxFF,OxFF,OxFF},{ OxFF,0xFF,OxFF,OxFF},{ OxFF,0xFF,OxFF,OxFF}}; //将一行的4个按键值移入缓冲区 keybuf [ keyout][0] = (keybuf [ keyout][0]<<1) l KEY_IN_1; keybuf [ keyout][1] = (keybuf [ keyout] [1] <<1) lKEY_IN_2; keybuf [ keyout][2]= (keybuf [ keyout][2]<< 1) | KEY_IN_3; keybuf [ keyout][3] = (keybuf [ keyout] [3] <<1) | KEY_IN_4;//消抖后更新按键状态 for (i=0; i<4 ; i++)//每行4个按键,所以循环4次 if ((keybuf [ keyout] [i] & Ox0F) == 0x00) { //连续4次扫描值为o,即4*4ms内都是按下状态时,可认为按键已稳定的按下 Keysta[ keyout] [i] =0; else if ( (keybuf [ keyout][i] & 0xOF) -= 0x0F) { //连续4次扫描值为1,即4*4ms内都是弹起状态时,可认为按键已稳定的弹起 Keysta [ keyout] [i] = l; } //执行下一次的扫描输出keyout++; //输出索引递增 keyout = keyout &0x03;/l索引值加到4即归零 switch (keyout) { //根据索引,释放当前输出引脚,拉低下次的输出引脚 case 0: KEY_OUT_4 = 1; KEY_OUT_1 = 0 ; break; case 1: KEY_OUT_1 = 1;KEY_OUT_2 = 0; break; ease 2: KEY_OUT_2 = 1;KEY_OUT_3 = 0; break; case 3: KEY_OUT_3 = 1;KEY_OUT_4 = 0; break; default : break; } } /*数码管动态扫描刷新函数,需在定时中断中调用*/void LedScan ( ) { static unsigned char i = 0;//动态扫描的索引 P0 = 0xFF;//显示消隐 switch(i) case 0: ADDR2=0 ;ADDR1=0; ADDRO=0 ; i++; PO=LedBuff[01; break; case 1: ADDR2=0; ADDR1=0; ADDRO=1; i++; PO=T.edRuff [1] ; break; case 2: ADDR2=0; ADDR1=1;ADDRO=0; i++; PO=LedBuff[2] ; break; case 3: ADDR2=0; ADDR1=1;ADDRO=1; i++;PO=LedBuff[3] ; break; case 4: ADDR2=1; ADDR1=0; ADDRO=0; i++;PO=LedBuff[4] ; break; case 5: ADDR2=1;ADDR1=0; ADDRO=1; i=0; PO=LedBuff [ 5] ; break; default : break; } ) /* TO中断服务函数,用于数码管显示扫描与按键扫描*/ void InterruptTimer0 ( ) interrupt 1 ( TH0 = 0xFC; //重新加载初值 TL0 = 0x67; LedScan ( ) ; //调用数码管显示扫描函数 Keyscan () ; //调用按键扫描函数 }
  • [2021暑期计划] 【2021暑期计划】我要每天学习单片机两小时,背40个六级单词。
    【2021暑期计划】我要每天学习单片机两小时,背40个六级单词。
  • [技术干货] 我们常说的中断和异常到底是什么
          在操作系统中引入核心态和用户态这两种工作状态后,就需要考虑这两种状态之间如何切换。操作系统内核工作在核心态,而用户程序工作在用户态。系统不允许用户程序实现核心态的功能,而它们又必须使用这些功能。因此,需要在核心态建立一些“门”,以便实现从用户态进入核心态。在实际操作系统中,CPU运行上层程序时唯一能进入这些“门”的途径就是通过中断或异常。发生中断或异常时,运行用户态的CPU会立即进入核心态,这是通过硬件实现的(例如,用一个特殊寄存器的一位来表示CPU所处的工作状态,0表示核心态,1表示用户态。若要进入核心态,则只需将该位置О即可)。中断是操作系统中非常重要的一个概念,对一个运行在计算机上的实用操作系统而言,缺少了中断机制,将是不可想象的。原因是,操作系统的发展过程大体上就是一个想方设法不断提高资源利用率的过程,而提高资源利用率就需要在程序并未使用某种资源时,把它对那种资源的占有权释放,而这一行为就需要通过中断实现。1.中断和异常的定义      中断(Interruption)也称外中断,指来自CPU执行指令以外的事件的发生,如设备发出的IO结束中断,表示设备输入/输出处理已经完成,希望处理机能够向设备发下一个输入/输出请求,同时让完成输入/输出后的程序继续运行。时钟中断,表示一个固定的时间片已到,让处理机处理计时、启动定时运行的任务等。这一类中断通常是与当前指令执行无关的事件,即它们与当前处理机运行的程序无关。关于内中断和外中断的联系与区别:      异常(Exception)也称内中断、例外或陷入( trap),指源自CPU执行指令内部的事件,如程序的非法操作码、地址越界、算术溢出、虚存系统的缺页及专门的陷入指令等引起的事件。对异常的处理一般要依赖于当前程序的运行现场,而且异常不能被屏蔽,一旦出现应立即处理。2、中断处理的过程不同计算机的中断(指外中断)处理过程各具特色,就其多数而论1)关中断。CPU响应中断后,首先要保护程序的现场状态,在保护现场的过程中,CPU 不应响应更高级中断源的中断请求。否则,若现场保存不完整,在中断服务程序结束后,也就不能正确地恢复并继续执行现行程序。2)保存断点。为保证中断服务程序执行完毕后能正确地返回到原来的程序,必须将原来的程序的断点(即程序计数器PC)保存起来。3)中断服务程序寻址。其实质是取出中断服务程序的入口地址送入程序计数器PC。4)保存现场和屏蔽字。进入中断服务程序后,首先要保存现场,现场信息一般是指程序状态字寄存器PSWR和某些通用寄存器的内容。5)开中断。允许更高级中断请求得到响应。6)执行中断服务程序。这是中断请求的目的。7)关中断。保证在恢复现场和屏蔽字时不被中断。8)恢复现场和屏蔽字。将现场和屏蔽字恢复到原来的状态。9)开中断、中断返回。中断服务程序的最后一条指令通常是一条中断返回指令,使其返回到原程序的断点处,以便继续执行原程序。      其中,1~3步是在CPU进入中断周期后,由硬件自动(中断隐指令)完成的;4~9步由中断服务程序完成。恢复现场是指在中断返回前,必须将寄存器的内容恢复到中断处理前的状态,这部分工作由中断服务程序完成。中断返回由中断服务程序的最后一条中断返回指令完成。
  • [技术干货] PHP 引用的概念
    什么是引用?在 PHP 中引用意味着用不同的名字访问同一个变量内容。它不是C的指针,保存的并不是内存地址,无法进行指针运算。引用只是符号表的别名。就像 Unix 系统中的硬链接, Windows 系统中的快捷方式。上面是官方手册中的原文,怎么说呢,引用其实和我们印象中的C里面的指针并不是相同的概念。指针是针对真实内存的操作,引用是针对指向这个内存的符号表的操作。还是从操作系统的快捷方式来说,快捷方式是可以删的,这就是PHP的引用。而C不仅删了快捷方式,还把原文件也给删了,这就是C的指针操作。// 引用不是指针 $a = 1; $b = &$a; echo $a, '===', $b, PHP_EOL; unset($b); echo $a, '===', $b, PHP_EOL;上面的代码是在PHP中,我们把b变量指向b变量指向a,作为a的引用变量。然后删除a的引用变量。然后删除b,对$a没有任何影响。#include <stdio.h> #include <stdlib.h> int main() { // C 中的指针和引用 int a = 1; int* b = &a; printf("%i\n", a); // 1 free(b); // free b printf("%i\n", a); //get error: *** error for object 0x7fff6350da08: pointer being freed was not allocated return 0; }而C中的引用指针就不行了,我们把b变量删掉后,再打印a变量就直接报错了。虽然说PHP的底层也是C写得,但我们都知道C中的指针是出了名的变态,没有一定的功底非常容易出错。所以PHP的开发者没有暴露C的原始指针能力,而是采用了和Java之类的类似的引用能力。这也是现代语言的特性,不需要我们过多的关注过于底层的能力,而将更多的时间放在业务实现上。引用在数组和对象中的使用如果具有引用的数组被拷贝,其值不会解除引用。对于数组传值给函数也是如此。$arr1 = ["a", "b"]; $t1 = &$arr1[1]; $arr2 = $arr1; $arr2[1] = "c"; var_dump($arr1); // array(2) { // [0]=> // string(1) "a" // [1]=> // &string(1) "c" // } $arr1 = ["a", "b"]; $t1 = &$arr1[1]; unset($t1); // unset 掉引用 $arr2 = $arr1; $arr2[1] = "c"; var_dump($arr1); // array(2) { // [0]=> // string(1) "a" // [1]=> // string(1) "b" // }这个其实挺有意思的,我们对比这两个例子可以看出一个问题,t变量指向t变量指向arr[1]的引用。arr2直接=这个arr2直接=这个arr1,没有使用引用,然后arr2修改了arr2修改了arr2[1]的内容,arr1相应的内容也发生了改变,如果unset掉arr1相应的内容也发生了改变,如果unset掉t变量,则$arr1相应的内容就不会发生改变。对此,我在文档中找到了下面的解释:由于PHP内部工作的特殊性,如果对数组的单个元素进行引用,然后复制数组,无论是通过赋值还是通过函数调用中的值传递,都会将引用复制为数组的一部分。这意味着对任一数组中任何此类元素的更改都将在另一个数组(和其他引用中)中重复,即使数组具有不同的作用域(例如,一个是函数内部的参数,另一个是全局的)!在复制时没有引用的元素,以及在复制数组后分配给其他元素的引用,将正常工作(即独立于其他数组)。不仅仅是数组,对象的引用也会有一些好玩的问题。$o1 = new stdClass(); $o1->a = 'a'; var_dump($o1); // object(stdClass)#1 (1) { // ["a"]=> // string(1) "a" // } $o2 = &$o1; $o3 = $o1; $o2->a = 'aa'; var_dump($o1); // object(stdClass)#1 (1) { // ["a"]=> // string(2) "aa" // } var_dump($o3); // $o2修改了$a为'aa',$o3也变成了'aa' // object(stdClass)#1 (1) { // ["a"]=> // string(2) "aa" // } $o1->a = 'aaa'; $o1 = null; var_dump($o2); // $o2引用变成了null // NULL var_dump($o3); // $o3不仅引用还存在,并且$a变成了'aaa' // object(stdClass)#1 (1) { // ["a"]=> // string(3) "aaa" // }上面例子中有三个对象,o1、o1、o2、o3,其中,o3,其中,o2是对o1的引用,o1的引用,o3是直接赋值为o1。对o1。对o2属性的操作不仅会反映在o1中,也会反映到o1中,也会反映到o3中。其实我们之前专门有一篇文章就讲的这个问题,首先对象默认赋值就是引用,其次这个例子很好地证明了引用就是一个符号表的绑定。删除了快捷方式对原始对象和其他快捷方式没有任何影响。引用的传递关于引用在方法参数上的传递,最重要的是记住两点:一是方法内部修改了变量外部也会变,这是引用的特性嘛;二是只能传递变量、New 语句、从函数中返回的引用三种类型。error_reporting(E_ALL); function foo(&$var) { $var++; echo 'foo:', $var; } function bar() // Note the missing & { $a = 5; return $a; } foo(bar()); // 自 PHP 5.0.5 起导致致命错误,自 PHP 5.1.1 起导致严格模式错误 // 自 PHP 7.0 起导致 notice 信息,Notice: Only variables should be passed by reference foo($a = 5); // 表达式,不是变量, Notice: Only variables should be passed by reference // foo(5); // 导致致命错误 !5是个常量! /////////////////////////////// // 正确的传递类型 $a = 5; foo($a); // 变量 function &baz() { $a = 5; return $a; } foo(baz()); // 从函数中返回的引用 function foo1(&$var) { print_r($var); } foo1(new stdClass()); // new 表达式引用的返回引用的返回并不是经常使用的一个能力。文档中的原文是:不要用返回引用来增加性能,引擎足够聪明来自己进行优化。仅在有合理的技术原因时才返回引用!$a = 1; function &test(){ global $a; return $a; } $b = &test($a); $b = 2; echo $a, PHP_EOL;当你想要返回一个引用变量的时候,一定要给方法定义和方法调用的时候都使用&符号。这个是需要注意的点。当其他地方修改原本的变量值或者返回的变量值经过修改后,都会影响到所有调用这个值的地方。所以说,引用的返回是比较危险的,因为你不清楚什么时候在什么地方这个值可能发生了修改,对于bug的排查会非常困难。引用的取消取消引用其实就是直接unset掉变量就可以了。但是一定要记住,PHP中的引用是指向的符号表,对原始真实的值是不起作用的,所以即使unset掉了最原始的那个变量,对其它引用赋值的变量也不会有影响!!$a = 1; $b = &$a; $c = &$b; $b = 2; echo '定义引用后:', $a, '===', $b, '===', $c, PHP_EOL; unset($b); $b = 3; echo '取消$b的引用,不影响$a、$c:', $a, '===', $b, '===', $c, PHP_EOL; $b = &$a; unset($a); echo '取消$a,不影响$b、$c:', $a, '===', $b, '===', $c, PHP_EOL; // 定义引用后:2===2===2 // 取消$b的引用:2===3===2 // 取消$a,不影响$c:===3===2 $a = 1; $b = & $a; $c = & $b; // $a, $b, $c reference the same content '1' $a = NULL; // All variables $a, $b or $c are unset echo '所有引用成空:', $a, '===', $b, '===', $c, PHP_EOL;
  • [技术干货] harmonyOS基于HiSpark Wifi loT开发套件(HI3861开发版)玩转指南
    玩转指南HiSpark Wifi loT 开发套件 炫彩灯板三色LED——PWM占空比控制颜色1.将炫彩灯板的三色灯依次按照红,绿,蓝的顺序闪烁三次2.使用PWM对炫彩灯版的三色灯进行控制2.1 什么是PWM2.2 代码光敏电阻的使用1.涉及到的主要API2.寻找光敏电阻对应的端口3.代码4.演示视频人体红外传感器的使用1.寻找人体红外传感器对应的端口2.代码3.演示视频HiSpark Wifi IoT 开发套件 OLED板OLED屏幕输出1.涉及到的主要API2.寻找OLED屏幕对应的端口3.代码4.演示视频harmonyOS虽然现在处于刚刚发布的阶段,发布会上的好多的宣传都是美好的,虽然现在还没完全发布出来,也有好多人也持有怀疑态度。不过正如好多人所说,如果这些美好的愿景,在中国有一个公司能做到的话,应该也只剩华为了。HiSpark Wifi loT 开发套件 炫彩灯板三色LED——PWM占空比控制颜色GPIO10:红GPIO11:绿GPIO12:蓝1.将炫彩灯板的三色灯依次按照红,绿,蓝的顺序闪烁三次代码:#include<stdio.h>#include<unistd.h>#include "ohos_init.h"#include "cmsis_os2.h"#include "wifiiot_gpio.h"#include "wifiiot_gpio_ex.h"#include "wifiiot_pwm.h"#include "wifiiot_adc.h"#include "wifiiot_errno.h"#define HUMAN_SENSOR_CHAN_NAME WIFI_IOT_ADC_CHANNEL_3#define RED_LED_PIN_NAME WIFI_IOT_IO_NAME_GPIO_10#define RED_LED_PIN_FUNCTION WIFI_IOT_IO_FUNC_GPIO_10_GPIO#define GREEN_LED_PIN_NAME WIFI_IOT_IO_NAME_GPIO_11#define GREEN_LED_PIN_FUNCTION WIFI_IOT_IO_FUNC_GPIO_11_GPIO#define BLUE_LED_PIN_NAME WIFI_IOT_IO_NAME_GPIO_12#define BLUE_LED_PIN_FUNCTION WIFI_IOT_IO_FUNC_GPIO_12_GPIO#define LED_DELAY_TIME_US 300000#define LED_BRIGHT WIFI_IOT_GPIO_VALUE1#define LED_DARK WIFI_IOT_GPIO_VALUE0#define NUM_BLINKS 2static void ColorfulLightTask(void *arg){    (void)arg;    static const WifiIotGpioIdx pins[] = {RED_LED_PIN_NAME,GREEN_LED_PIN_NAME,BLUE_LED_PIN_NAME};    for(int i = 0 ; i < NUM_BLINKS ; i++){        for(unsigned j = 0 ; j < sizeof(pins)/sizeof(pins[0]);j++){            GpioSetOutputVal(pins[j],LED_BRIGHT);            usleep(LED_DELAY_TIME_US);            GpioSetOutputVal(pins[j],LED_DARK);            usleep(LED_DARK);        }    }}static void ColorfulLightDemo(void){    osThreadAttr_t attr;    GpioInit();    IoSetFunc(RED_LED_PIN_NAME,RED_LED_PIN_FUNCTION);    IoSetFunc(GREEN_LED_PIN_NAME,GREEN_LED_PIN_FUNCTION);    IoSetFunc(BLUE_LED_PIN_NAME,BLUE_LED_PIN_FUNCTION);    GpioSetDir(RED_LED_PIN_NAME,WIFI_IOT_GPIO_DIR_OUT);    GpioSetDir(GREEN_LED_PIN_NAME,WIFI_IOT_GPIO_DIR_OUT);    GpioSetDir(BLUE_LED_PIN_NAME,WIFI_IOT_GPIO_DIR_OUT);    attr.name="ColorfulLightTask";    attr.attr_bits=0U;    attr.cb_mem=NULL;    attr.cb_size=0U;    attr.stack_mem=NULL;    attr.stack_size=4096;    attr.priority=osPriorityNormal;    if(osThreadNew(ColorfulLightTask,NULL,&attr) == NULL){        printf("[ColorfulLightDemo] Failed to create ColorfulLightTask!\n");    }}APP_FEATURE_INIT(ColorfulLightDemo);HI3861开发版炫彩灯板演示视频2.使用PWM对炫彩灯版的三色灯进行控制2.1 什么是PWM因为我之前无论是学习还是工作确实有没有涉及到太硬件的东西,所以确实连个PWM是个什么都不清楚。熟悉硬件的大佬,这部分直接跳过就OK了。PWM是Pulse Width Modulation的缩写,它的中文名字是脉冲宽度调制,利用微处理器的数字输出来对模拟电路进行控制的一种有效的技术。好了,这就是它的概念了,再说一些专业解释,估计大家也不想听了。接下来我就按照自己的理解来说了。看上图,这是一个周期为10ms,即频率是100Hz的波形。上图共显示了三个周期,其中第1个周期高电平为4ms,第二个周期高电平为6ms,第三个周期高电平为8ms,可以看到3个周期中,高电平所占单个周期的比例越来越高。好了,这个比例越来越高有什么用呢?接下来就是重点了:首先明白一点我们人体的肉眼,每秒有超过固定张数的图片闪过时,我们是感觉不到顿挫感的,电影就是整个原理。同理,当小灯以超过100Hz的频率进行闪烁时,在我们人眼看来就是一直亮着的。所以当用高电平来控制小灯亮灭的时候,上一段所提到的高电平所占比例越高就会发生作用,随着它的比例越高就会灯就会越亮。不知道大家能不能明白,不明白的话就直接去度娘查吧,或者有高手也可以在留言里指导我一波如何解释这个东东。2.2 代码#define LED_DELAY_TIME_US 300000#define LED_BRIGHT WIFI_IOT_GPIO_VALUE1#define LED_DARK WIFI_IOT_GPIO_VALUE0#define NUM_BLINKS 2#define PWM_FREQ_DIVITION 64000static void ColorfulLightTask(void *arg){    (void)arg;    IoSetFunc(RED_LED_PIN_NAME,WIFI_IOT_IO_FUNC_GPIO_10_PWM1_OUT);    IoSetFunc(GREEN_LED_PIN_NAME,WIFI_IOT_IO_FUNC_GPIO_11_PWM2_OUT);    IoSetFunc(BLUE_LED_PIN_NAME,WIFI_IOT_IO_FUNC_GPIO_12_PWM3_OUT);    PwmInit(WIFI_IOT_PWM_PORT_PWM1);    PwmInit(WIFI_IOT_PWM_PORT_PWM2);    PwmInit(WIFI_IOT_PWM_PORT_PWM3);   while(1){       for(int i = 1 ; i <= PWM_FREQ_DIVITION ; i *= 2){           PwmStart(WIFI_IOT_PWM_PORT_PWM1,i,PWM_FREQ_DIVITION);           usleep(250000);           PwmStop(WIFI_IOT_PWM_PORT_PWM1);       }        for(int i = 1 ; i <= PWM_FREQ_DIVITION ; i *= 2 ){           PwmStart(WIFI_IOT_PWM_PORT_PWM2,i,PWM_FREQ_DIVITION);            usleep(250000);            PwmStop(WIFI_IOT_PWM_PORT_PWM2);        }        for(int i = 1 ; i <= PWM_FREQ_DIVITION ; i *= 2){           PwmStart(WIFI_IOT_PWM_PORT_PWM3,i,PWM_FREQ_DIVITION);           usleep(250000);           PwmStop(WIFI_IOT_PWM_PORT_PWM3);       }   }}static void ColorfulLightDemo(void){    osThreadAttr_t attr;    GpioInit();    attr.name="ColorfulLightTask";    attr.attr_bits=0U;    attr.cb_mem=NULL;    attr.cb_size=0U;    attr.stack_mem=NULL;    attr.stack_size=4096;    attr.priority=osPriorityNormal;    if(osThreadNew(ColorfulLightTask,NULL,&attr) == NULL){        printf("[ColorfulLightDemo] Failed to create ColorfulLightTask!\n");    }}APP_FEATURE_INIT(ColorfulLightDemo);首先在编译过程中会报如图所示的错误:解决方法:解决: 修改vendor\hisi\hi3861\hi3861\build\config\usr_config.mk文件中的#CONFIG_PWM_SUPPORT is not set修改为CONFIG_PWM_SUPPORT=y烧写完成,演示视频:Hi3861OLED灯板演示光敏电阻的使用首先说一下目标:通过光敏电阻的使用来控制绿灯的亮度。也就是随着光照的增强,绿灯的亮度也增强。1.涉及到的主要APIAdcRead (WifiIotAdcChannelIndex channel, unsigned short *data, WifiIotAdcEquModelSel equModel, WifiIotAdcCurBais curBais, unsigned short rstCnt)这个API是核心函数,从字面意思是读取模拟信号准换成数字信号之后的数据的。第一个参数是WifiIotAdcChannelIndex,这个参数是用来指定从哪个通道来读取;第二个参数是unsigned short *,这个参数是用来存放读取到的数字。第三个参数是WifiIotAdcEquModelSel,这个参数是设置读取方式的吧,第四个参数是WifiIotAdcCurBais,这个参数字面意思是模拟能量控制方式,应该也是一个设置项;第五个参数是unsigned short,这个参数应该是设置一些复位键盘相关的操作,了解的大佬可以批评指出。2.寻找光敏电阻对应的端口从上面的AdcRead函数的第一个参数就可以看出,需要得到光敏电阻对应的通道口号。下面我们来一步一步分析怎么去找?(ps:没搞过硬件,各位大佬觉得有问题,欢迎批评指出)1.先找到光敏电阻的引脚从这个图上可以看出光敏电阻的引脚,也就是插到炫彩灯版的引脚是叫做PHO_RES2.再找到炫彩灯版上与光敏电镀引脚对应的引脚因为光敏电阻的引脚是PHO_RES,所以在炫彩灯版上对应的引脚是8,好的,最后再来看主板对应的引脚3.在主板上找炫彩灯版引脚8对应的引脚。炫彩灯版的引脚8对应的就是主板上从左上角第三个口,从中可以看到是ADC4,所以恭喜大家,这就找到了。这里所对应的就是AdcRead函数中的第一个参数WIFI_IOT_ADC_CHANNEL_43.代码#include<stdio.h>#include<unistd.h>#include "ohos_init.h"#include "cmsis_os2.h"#include "wifiiot_gpio.h"#include "wifiiot_gpio_ex.h"#include "wifiiot_pwm.h"#include "wifiiot_adc.h"#include "wifiiot_errno.h"#define HUMAN_SENSOR_CHAN_NAME WIFI_IOT_ADC_CHANNEL_3#define RED_LED_PIN_NAME WIFI_IOT_IO_NAME_GPIO_10#define RED_LED_PIN_FUNCTION WIFI_IOT_IO_FUNC_GPIO_10_GPIO#define GREEN_LED_PIN_NAME WIFI_IOT_IO_NAME_GPIO_11#define GREEN_LED_PIN_FUNCTION WIFI_IOT_IO_FUNC_GPIO_11_GPIO#define BLUE_LED_PIN_NAME WIFI_IOT_IO_NAME_GPIO_12#define BLUE_LED_PIN_FUNCTION WIFI_IOT_IO_FUNC_GPIO_12_GPIO#define LED_DELAY_TIME_US 300000#define LED_BRIGHT WIFI_IOT_GPIO_VALUE1#define LED_DARK WIFI_IOT_GPIO_VALUE0#define NUM_BLINKS 2#define PWM_FREQ_DIVITION 64000#define ADC_RESOLUTION 4996static void ColorfulLightTask(void *arg){    (void)arg;    IoSetFunc(RED_LED_PIN_NAME,WIFI_IOT_IO_FUNC_GPIO_10_PWM1_OUT);    IoSetFunc(GREEN_LED_PIN_NAME,WIFI_IOT_IO_FUNC_GPIO_11_PWM2_OUT);    IoSetFunc(BLUE_LED_PIN_NAME,WIFI_IOT_IO_FUNC_GPIO_12_PWM3_OUT);    PwmInit(WIFI_IOT_PWM_PORT_PWM1);    PwmInit(WIFI_IOT_PWM_PORT_PWM2);    PwmInit(WIFI_IOT_PWM_PORT_PWM3);       while(1){       unsigned short data = 0;       unsigned short duty = 0;       if(AdcRead(WIFI_IOT_ADC_CHANNEL_4,&data,WIFI_IOT_ADC_EQU_MODEL_4,WIFI_IOT_ADC_CUR_BAIS_DEFAULT,0)==WIFI_IOT_SUCCESS)       {           printf("data:%d",data);           //128 1820           duty = PWM_FREQ_DIVITION * (1948-(unsigned int)data) / ADC_RESOLUTION;       }       PwmStart(WIFI_IOT_PWM_PORT_PWM2,duty,PWM_FREQ_DIVITION);       usleep(10000);       PwmStop(WIFI_IOT_PWM_PORT_PWM2);   }}static void ColorfulLightDemo(void){    osThreadAttr_t attr;    GpioInit();    attr.name="ColorfulLightTask";    attr.attr_bits=0U;    attr.cb_mem=NULL;    attr.cb_size=0U;    attr.stack_mem=NULL;    attr.stack_size=4096;    attr.priority=osPriorityNormal;    if(osThreadNew(ColorfulLightTask,NULL,&attr) == NULL){        printf("[ColorfulLightDemo] Failed to create ColorfulLightTask!\n");    }}APP_FEATURE_INIT(ColorfulLightDemo);4.演示视频Hi3861基于鸿蒙系统的使用光鲜传感器控制灯的亮度人体红外传感器的使用首先说一下,使用人体传感器要做的一个实现现象是:通过人体传感器控制红色led灯的亮度,距离现象是当人体传感器检测到有人靠近时候,红色的led灯亮度加强,当人离开的时候,红色的led灯的亮度减弱。由于人体红外传感器的使用和光敏电阻的使用都是涉及到了Adc信号的读取,并且所涉及的到的主要的API都是AdcRead(点击跳转至本文之前的解释),所以API相关可以直接去看光敏电阻使用部分的介绍1.寻找人体红外传感器对应的端口1.首先,从最开始来找人体红外传感器的引脚从上面这个图看以看出人体红外传感器的引脚是REL,也就是插到炫彩灯版上的引脚是REL2.然后再找炫彩灯版上与REL引脚对应的引脚从上图中可以看出,炫彩灯版REL对应的是从右边开始数的第二个引脚。3.然后再寻找与炫彩灯版右边开始数的第二个引脚在主板上所对应的引脚从上图中可以看出炫彩灯版右边开始数第二个引脚对应的为ADC3,因为就确定到了人体红外传感器所对应的端口。2.代码#include<stdio.h>#include<unistd.h>#include "ohos_init.h"#include "cmsis_os2.h"#include "wifiiot_gpio.h"#include "wifiiot_gpio_ex.h"#include "wifiiot_pwm.h"#include "wifiiot_adc.h"#include "wifiiot_errno.h"#define PWM_FREQ_DIVITION 64000#define ADC_RESOLUTION 4996static void ColorfulLightTask(void *arg){    (void)arg;    //由于炫彩灯板中红色led灯的GIPO为10,这里初始化控制GPIO10的控制方式为PWM模式    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_10,WIFI_IOT_IO_FUNC_GPIO_10_PWM1_OUT);    //调用函数初始化PWM模式    PwmInit(WIFI_IOT_PWM_PORT_PWM1);       while(1){       unsigned short data = 0;       unsigned short duty = 0;       //使用AdcRead函数对3通道进行ADC信号读取,读取到的结果存储在data中       if(AdcRead(WIFI_IOT_ADC_CHANNEL_3,&data,WIFI_IOT_ADC_EQU_MODEL_4,WIFI_IOT_ADC_CUR_BAIS_DEFAULT,0)==WIFI_IOT_SUCCESS)       {           printf("data:%d",data);           duty = PWM_FREQ_DIVITION *(unsigned int)data / ADC_RESOLUTION;           //128 1820            //duty = PWM_FREQ_DIVITION * (1948-(unsigned int)data) / ADC_RESOLUTION;       }       //PWM模式开启对红色led灯的控制       PwmStart(WIFI_IOT_PWM_PORT_PWM1,duty,PWM_FREQ_DIVITION);       usleep(10000);       //PWM模式关闭对红色led灯的控制       PwmStop(WIFI_IOT_PWM_PORT_PWM1);   }}static void ColorfulLightDemo(void){    osThreadAttr_t attr;    GpioInit();    attr.name="ColorfulLightTask";    attr.attr_bits=0U;    attr.cb_mem=NULL;    attr.cb_size=0U;    attr.stack_mem=NULL;    attr.stack_size=4096;    attr.priority=osPriorityNormal;    if(osThreadNew(ColorfulLightTask,NULL,&attr) == NULL){        printf("[ColorfulLightDemo] Failed to create ColorfulLightTask!\n");    }}APP_FEATURE_INIT(ColorfulLightDemo);3.演示视频通过实验现象发现,该人体传感器的只能读到两个数据,当人体靠近(大概7-8厘米就可以识别到)时候,data的值为1844左右,识别不到人体靠近的时候该值为126左右。人体红外传感器不是很灵敏Hi3861基于鸿蒙系统的使用人体红外传感器控制灯的亮度HiSpark Wifi IoT 开发套件 OLED板OLED屏幕输出首先来实现一个最低级别的目标:OLED屏幕上输出hello,world为了能够显示的清晰再加上一个计秒的数字。1.涉及到的主要API首先要搞清楚使用OLED板输出内容是使用的I2C协议unsigned int I2cInit (WifiIotI2cIdx id, unsigned int baudrate):用指定的波特率初始化I2C设备unsigned int I2cWrite (WifiIotI2cIdx id, unsigned short deviceAddr, const WifiIotI2cData * i2cData ):往一个I2C设备中写数据第一个参数:指定要写入的I2C设备ID第二个参数:指定要写入的设置的地址,也就是往哪儿写第三个参数:指定要写入数据的描述指针,也就是写的内容2.寻找OLED屏幕对应的端口1.首先看OLED屏幕相关的引脚,因为我们所涉及的是I2C通信协议,所以我们只看I2C相关的引脚,目前我的wifiiot的oled只支持I2C,I2C的两个引脚为SCL,SDA,一个用来控制信号,一个用来控制数据。从下图可以看到OLED屏幕SCL和SDA的引脚分别为3和4.2.然后再确定OLED屏幕的SCL和SDA这两个引脚分别插到OLED板上的哪个引脚从下图可以看出是右边开始数的第三个和第四个3.最后再确定OLED板右边开始的第3,4个引脚会插入到主板上的哪两个引脚?从下图可以看出是主板依旧从右往左数的第3,4个引脚即为GPIO13和GPIO143.代码#include<stdio.h>#include<unistd.h>#include "ohos_init.h"#include "cmsis_os2.h"#include "wifiiot_gpio.h"#include "wifiiot_gpio_ex.h"#include "wifiiot_i2c.h"#include "wifiiot_errno.h"#define OLED_I2C_IDX WIFI_IOT_I2C_IDX_0#define OLED_I2C_ADDR 0x78 // 默认地址为 0x78#define OLED_I2C_CMD 0x00 // 0000 0000       写命令#define OLED_I2C_DATA 0x40 // 0100 0000(0x40) 写数据#define OLED_I2C_BAUDRATE (400*1000) // 400k#define ARRAY_SIZE(a) sizeof(a)/sizeof(a[0])static unsigned char F6x8[][6] ={    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }, // sp    { 0x00, 0x00, 0x00, 0x2f, 0x00, 0x00 }, // !    { 0x00, 0x00, 0x07, 0x00, 0x07, 0x00 }, // "    { 0x14, 0x14, 0x14, 0x14, 0x14, 0x14 }, // horiz lines};/****************************************8*16的点阵************************************/static const unsigned char F8X16[]={    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,// 0    0x00,0x00,0x00,0xF8,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x33,0x30,0x00,0x00,0x00,//! 1    0x00,0x10,0x0C,0x06,0x10,0x0C,0x06,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,//" 2    0x40,0xC0,0x78,0x40,0xC0,0x78,0x40,0x00,0x04,0x3F,0x04,0x04,0x3F,0x04,0x04,0x00,//# 3    0x00,0x70,0x88,0xFC,0x08,0x30,0x00,0x00,0x00,0x18,0x20,0xFF,0x21,0x1E,0x00,0x00,//$ 4};enum Font {    FONT6x8 = 1,    FONT8x16};typedef enum Font Font;static uint32_t I2cWiteByte(uint8_t regAddr, uint8_t byte){    WifiIotI2cIdx id = OLED_I2C_IDX;    uint8_t buffer[] = {regAddr, byte};    WifiIotI2cData i2cData = {0};    i2cData.sendBuf = buffer;    i2cData.sendLen = sizeof(buffer)/sizeof(buffer[0]);    return I2cWrite(id, OLED_I2C_ADDR, &i2cData);}/** * @brief Write a command byte to OLED device. * * @param cmd the commnad byte to be writen. * @return Returns {@link WIFI_IOT_SUCCESS} if the operation is successful; * returns an error code defined in {@link wifiiot_errno.h} otherwise. */static uint32_t WriteCmd(uint8_t cmd){    return I2cWiteByte(OLED_I2C_CMD, cmd);}/** * @brief Write a data byte to OLED device. * * @param cmd the data byte to be writen. * @return Returns {@link WIFI_IOT_SUCCESS} if the operation is successful; * returns an error code defined in {@link wifiiot_errno.h} otherwise. */static uint32_t WriteData(uint8_t data){    return I2cWiteByte(OLED_I2C_DATA, data);}uint32_t OledInit(void){    //将GPIO13设置为i2c输出    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_13,WIFI_IOT_IO_FUNC_GPIO_13_I2C0_SDA);    //将GPIO14设置为i2c输出    IoSetFunc(WIFI_IOT_IO_NAME_GPIO_14,WIFI_IOT_IO_FUNC_GPIO_14_I2C0_SCL);    //使用指定波特率初始化I2C设备    I2cInit(WIFI_IOT_I2C_IDX_0,400*1000);    static const uint8_t initCmd[]={        0xAE,        0x00,        0x10,        0x40,        0xB0,        0x81,        0xFF,        0xA1,        0xA6,        0xA8,        0x3F,        0xC8,        0xD3,        0x00,        0xD5,        0x80,        0xD8,        0x05,        0xD9,        0xF1,        0xDA,        0x12,        0xDB,        0x30,        0x8D,        0x14,        0xAF,    };    for(size_t i = 0 ; i < sizeof(initCmd)/sizeof(initCmd[0]) ; i++){        WifiIotI2cData data = {0};        uint8_t buffer[] = {0x00,initCmd[i]};        data.sendBuf = buffer;        data.sendLen = sizeof(buffer)/sizeof(buffer[0]);        uint32_t status = I2cWrite(WIFI_IOT_I2C_IDX_0,0x78,&data);        if(status != WIFI_IOT_SUCCESS)            return status;    }    return WIFI_IOT_SUCCESS;}void OledFillScreen(uint8_t fillData){    uint8_t m = 0 ;    uint8_t n = 0;    for(m=0;m<8;m++){;        WifiIotI2cData data = {0};        uint8_t buffer[] = {0x00,0xb0+m};        data.sendBuf = buffer;        data.sendLen = sizeof(buffer)/sizeof(buffer[0]);        I2cWrite(WIFI_IOT_I2C_IDX_0,0x78,&data);        data.sendBuf[1] = 0x00;        I2cWrite(WIFI_IOT_I2C_IDX_0,0x78,&data);        data.sendBuf[1] = 0;        I2cWrite(WIFI_IOT_I2C_IDX_0,0x78,&data);        for(n = 0; n < 128;n++)        {            data.sendBuf[0] = 0x40;            data.sendBuf[1] = fillData;            I2cWrite(WIFI_IOT_I2C_IDX_0,0x78,&data);        }    }}void OledSetPosition(uint8_t x, uint8_t y){    WriteCmd(0xb0 + y);    WriteCmd(((x & 0xf0) >> 4) | 0x10);    WriteCmd(x & 0x0f);}void OledShowChar(uint8_t x, uint8_t y, uint8_t ch, Font font){              uint8_t c = 0;    uint8_t i = 0;    c = ch - ' '; //得到偏移后的值        if (x > 128 - 1) {        x = 0;        y = y + 2;    }    if (font == FONT8x16) {        OledSetPosition(x, y);            for (i = 0; i < 8; i++){            WriteData(F8X16[c*16 + i]);        }        OledSetPosition(x, y+1);        for (i = 0; i < 8; i++) {            WriteData(F8X16[c*16 + i + 8]);        }    } else {        OledSetPosition(x, y);        for (i = 0; i < 6; i++) {            WriteData(F6x8[c][i]);        }    }}void OledShowString(uint8_t x, uint8_t y, const char* str, Font font){    uint8_t j = 0;    if (str == NULL) {        printf("param is NULL,Please check!!!\r\n");        return;    }    while (str[j]) {        OledShowChar(x, y, str[j], font);        x += 8;        if (x > 120) {            x = 0;            y += 2;        }        j++;    }}static void OledTask(void *arg){    (void)arg;    //初始化GPIO设备    GpioInit();    //该函数定义了初始化OLED屏幕的一些操作    OledInit();     OledFillScreen(0x00);    int i = 0;    static char text[128] = {0};    while(1)    {        i++;        snprintf(text,sizeof(text),"hello,world--%d--",i);        // OledShowString(0, 0, "Hello, HarmonyOS", 1);        OledShowString(0,0,text,1);        sleep(1);    }}static void OledDemo(void){    //定义一个线程来跑实验任务    osThreadAttr_t attr;    attr.name = "OledTask";    attr.attr_bits = 0U;    attr.cb_mem = NULL;    attr.cb_size = 0U;    attr.stack_mem = NULL;    attr.stack_size = 4096;    attr.priority = osPriorityNormal;    //给定义的线程绑定了一个OledTask任务,OledTask函数中会放具体要跑的任务代码的逻辑    if(osThreadNew(OledTask,NULL,&attr) == NULL){        printf("[OLED Demo] Failed to create OledTask!\n");    }}APP_FEATURE_INIT(OledDemo);————————————————原文链接:https://blog.csdn.net/qq130106486/article/details/109624925
  • [问题求助] 来点技术讨论
    就是最近我做了一个机械臂 基本功能可以实现 现在有二个问题 希望大佬们给点思路:1:首先我用了小熊派和树莓派 树莓派识别物体的颜色形状 串口通信给小熊派 然后 机械臂完成动作 现在遇到一个问题(我的摄像头是单目)就是如何让机械臂能够自适应 简单来说就是 我在摄像头能够识别的范围内 任意放一个小木块 机械臂能够大概的准确去抓取 我的思路是 摄像头 与 目标物体 在同一水平面 然后 (opencv) 得到目标物体的中心点 通过中心点与画面的X轴 差值 来计算偏转角度  问题在于 这个角度如何求 想的是 对边比斜边 目标物体的实际长度 比上摄像头到目标物体的距离 得到的度数 转化成占空比 传给小熊派 大概思路是这样  就是说这个思路或者方法是否有问题 因为我得到的度数不稳定就是与实际度数有时候相符合有时候离谱   希望大佬能给一个其他的思路来解决 自适应的问题 或者如果思路准确 方法出了什么问题2 第二个问题是 我的机械臂是用16路舵机控制板驱动 我想的是如果让小熊派去驱动6个舵机 是否有足够大的电压带动6个舵机  如果能驱动外接电池是否可能烧坏小熊派 就是说机械臂应该交给16路舵机板来控制 还是小熊派来控制 因为我想的是 如果用小熊派驱动可能会更加方便主要是还是自适应的问题 希望大佬们给点思路
  • [技术干货] 晶振&amp;复位电路
    晶振,又叫晶体振荡器,从这个名字我们就可以看出来,它注定一生都要不停振荡的。他起到的作用是为单片机系统提供基准时钟信号,单片机内部所有的工作都是以这个时钟信号为步调基准来进行工作的。STC89C52 单片机的 18 脚和 19 脚是晶振引脚,我们接了一个 11.0592M 的晶振(它每秒钟振荡 11,059,200 次),外加两个 20pF 的电容,电容的作用是帮助晶振起振,并维持振荡信号的稳定。复位电路上图是一个复位电路,接到了单片机的9脚RST(Reset)复位引脚上。单片机复位一般是3种情况:上电复位、手动复位、程序自动复位。假如我们的单片机程序有100行,当某一次运行到第50行的时候,突然停电了,这个时候单片机内部有的区域数据会丢失掉,有的区域数据可能还没丢失。那么下次打开设备的时候,我们希望单片机能正常运行,所以上电后,单片机要进行一个内部的初始化过程,这个过程就可以理解为上电复位,上电复位保证单片机每次都从一个固定的相同的状态开始工作。这个过程跟我们打开电脑电源开电脑的过程是一致的。当我们的程序运行时,如果遭受到意外干扰而导致程序死机,或者程序跑飞的时候,我们就可以按下一个复位按键,让程序重新初始化重新运行,这个过程就叫做手动复位,最典型的就是我们电脑的重启按钮。当程序死机或者跑飞的时候,我们的单片机往往有一套自动复位机制,比如看门狗,具体应用以后再了解。在这种情况下,如果程序长时间失去响应,单片机看门狗模块会自动复位重启单片机.。还有一些情况是我们程序故意重启复位单片机.。电源、晶振、复位构成了单片机最小系统的三要素,也就是说,一个单片机具备了这三个条件,就可以运行我们下载的程序了,其他的比如LED小灯、数码管、液晶等设备都是属于单片机的外部设备,即外设。最终完成我们想要的功能就是通过对单片机编程来控制各种各样的外设实现的。
  • [问题求助] 【LiteOS-Stuidio】【支持的开发板】请问支持stm32f2x系列的自制单片机板吗
    【功能模块】我看LiteOS-Studio支持固定几种开发板。现在我自己有块板子是STM32F207的,请问可否用Lite-Studio来移植liteOS?【操作步骤&问题现象】我刚才试了下,如果直接按照移植步骤来,只能选STM32F103开发板,这样做出来的projectsettings.json中的参数全部都是stm32f103ze.我手动把这些参数改成stm32f207ze, 或者改成 target/stm32f2x.cfg 编译后参数又全部都被改回了stm32f103我的问题是,Lite-Studio是否可以用来开发指定开发板以外的 自制板子?芯片型号是否可以与开发板不同?【截图信息】【日志信息】(可选,上传日志内容或者附件)
  • [技术干货] 【技术长文】啥是“X86”
    x86泛指一系列基于Intel 8086且向后兼容的中央处理器指令集架构。最早的8086处理器于1978年由Intel推出,为16位微处理器。 该系列较早期的处理器名称是以数字来表示80x86。由于以“86”作为结尾,包括Intel 8086、80186、80286、80386以及80486,因此其架构被称为“x86”。由于数字并不能作为注册商标,因此Intel及其竞争者均在新一代处理器使用可注册的名称,如Pentium,来描述x86架构下的处理器产品。现时英特尔将其称为IA-32,全名为“Intel Architecture, 32-bit”,一般情形下指代32位的架构。 x86架构于1978年推出的Intel 8086中央处理器中首度出现,它是从Intel 8008处理器中发展而来的,而8008则是发展自Intel 4004的。8086在三年后为IBM PC所选用,之后x86便成为了个人电脑的标准平台,成为了历来最成功的CPU架构。 其他公司也有制造x86架构的处理器,计有Cyrix(现为威盛电子所收购)、NEC集团、IBM、IDT以及Transmeta。Intel以外最成功的制造商为AMD,其早先产品Athlon系列处理器的市场份额仅次于Intel Pentium。 8086是16位处理器;直到1985年32位的80386的开发,这个架构都维持是16位。接着一系列的处理器表示了32位架构的细微改进,推出了数种的扩展,直到2003年AMD对于这个架构发展了64位的扩展,并命名为AMD64。后来英特尔也推出了与之兼容的处理器,并命名为Intel 64。两者一般被统称为x86-64或x64,开创了x86的64位时代。 值得注意的是英特尔早在1990年代就与惠普合作提出了一种用在安腾系列处理器中的独立的64位架构,这种架构被称为IA-64。IA-64是一种崭新的系统,和x86架构完全没有相似性;不应该把它与x86-64或x64弄混。 x86架构是重要地可变指令长度的CISC(复杂指令集电脑,Complex Instruction Set Computer)。字组(word, 4位组)长度的存储器访问允许不对齐存储器地址,字组是以低位字节在前的顺序存储在存储器中。向后兼容性及Intel量产制程经常领先业界一直都是在x86架构的发展背后一股驱动力量(设计的需要决定了这项因素而常常导致批评,尤其是来自对手处理器的拥护者和理论界,他们对于一个被广泛认为是落后设计的架构的持续成功感到不解)。但在较新的微架构中,x86处理器会把x86指令转换为更像RISC的微指令再予执行,从而获得可与RISC比拟的超标量性能,而仍然保持向前兼容。x86架构的处理器一共有四种运行模式,分别是真實模式,保护模式,系统管理模式以及虚拟V86模式。 在这篇简短的文章中出现的指令和寄存器助忆符号的名称,都在Intel文件中有所指定以及使用在Intel汇编器(Assembler)中(和兼容的,比如微软的MASM、Borland的TASM、CAD-UL的as386等等)。一个以Intel语法指定的指令"mov al, 30h"与AT&T语法的"movb $0x30, %al"相当,都是会被转译为两个字节的机器代码"B0 30"(十六进制)。你可以发现在这段程序中的"mov"或"al",都是原来的Intel助忆符号。如果我们想要的话,我们可以写一个汇编器由代码'move immediate byte hexadecimally encoded 30 into low half of the first register'(移动立即值比特十六进制编码30到第一个寄存器的低半部位),来产生相同的机器代码。然而,传统上汇编器(Assembler)一直使用Intel的助忆符号。 运作模式 实模式 在实模式下,存储器的访问是被区段开来。为了得到最后20比特的存储器地址,要将区段的地址往左移动4位,并且加上偏移的地址。因此,实模式下总共可以寻址的空间是220字节,或者是1MB,于1979年是相当让人印象深刻的象征。在实模式下有两种寻址模式:near和far。在far模式,区段跟偏移都需要被指定;在near模式,只需要偏移模式被指定,而存储器区段是由适当的区段寄存器获得。以资料而言是使用DS寄存器,代码是CS寄存器,堆栈是SS寄存器。举个例子,如果DS是A000h且SI是5677h,DS:SI会指向存储器的绝对地址DS × 16 + SI = A5677h 在这种架构下,两对不同的区段/偏移可以指向一个相同的绝对地址。因此如果DS是A111h且SI是4567h,DS:SI会指向跟上一段相同的A5677h。除了异值同址重复性之外,这种架构无法同时一次拥有4个以上的区段。此外,CS、DS和SS是为了程序正确功能而必须的,因此仅仅只有ES可以被用来指向其他的地方。这种模式原本是为了与Intel 8085兼容,导致程序员永无止尽的痛苦。 除了以上所说的,8086也拥有16-bit的32K(其变种 Intel 8088 是8-bit的64K)输入输出空间,以及一个由硬件支持的64K(一个区段)存储器堆栈。只有words(2字节)可以被推入到堆栈中。堆栈是由存储器的上端往下成长,他的底端是由SS:SP指向。有256个中断,可以由硬件或是软件同时组成。中断是可以串连在一起,使用堆栈来存储返回被中断的程序地址。 16位保护模式 Intel 80286可以在不改变任何东西下,支持8086的实模式16位软件,然而它也支持额外的工作模式称为保护模式,可以将可寻址的物理内存扩展到16MB,可寻址的虚拟内存最大到1GB。这是使用节区寄存器来存储在节区表格中的索引值。处理器中有两个这样的表格,分别为GDT和LDT,每一个可以存储最多8192个节区的描述子,每一个节区可以给予最大到64KB的存储器访问。节区表格提供一个24位的基底地址(base address),可以用此基底地址增加想要的偏移量来创造出一个绝对地址。此外,每一个节区可以被赋予四种权限等级中的一种(称为"rings")。 尽管这个推出的功能是一项进步,但是他们并没有被广泛地使用,因为保护模式的操作系统无法运行当时的实模式软件。这样的能力只有在随后80386处理器的虚拟86模式中出现。 在同时,操作系统比如OS/2尝试使用类似乒乓的方法,让处理器在保护和实模式间切换。这样都会让电脑变慢且不安全,像是在实模式下的程序可以轻易地使电脑死机。OS/2也定义了限制性的程序设计规则允许"Family API"或"bound"程序可以在实模式或保护模式下运行。然而这是给原本为保护模式下设计的程序有关,反之则不然。保护模式程序并不支持节区选择子和物理内存之间的关系。有时候会错误地相信在16位保护模式下运行实模式的程序,导致IBM必须选择使用Intel保留给BIOS的中断调用。事实上这类的程序使用任意的选择子数值和使用在上面提到的“节区运算”的方式有关。 这个问题也在Windows 3.x上出现。这个推出版本想要在16位保护模式下运行程序,而先前的版本只能在实模式下运行。理论上,如果Windows 1.x或2.x程序是写得“适当”且避免使用节区运算的方式,它就有可能在真实和保护模式两者下运行。Windows程序一般来说都会避免节区运算,这是因为Windows实现出软件的虚拟内存方式,及当程序不运行时候,搬移存储器中的代码和资料,所以操作绝对地址的方式是很危险的;当程序不运行时,被认为要保持存储器区块的“handles”,这样的handles已经非常相当于保护模式的选择子。在保护模式下的Windows 3.0运行一个旧的程序,会触发一个警告对话盒,建议在实模式下运行Windows(推测还是仍然可以使用扩展存储器,可能是在80386机器用EMM386模拟,因此它并不被局限于640KB)或是从厂商那更新到新的版本。好的行为之程序可能可以使用特别的工具来避免这样的对话盒。不可能有些GUI程序在16位保护模式下运行,且其它GUI程序在实模式运行,可能是因为这会需要两个分开的环境且会依于前面所提到的处理器在两个模式间的乒乓效应。从Windows 3.1版开始,实模式就消失了。 32位保护模式 Intel 80386推出后,也许是到目前为止x86架构的最大跃进。除了需要值得注意的Intel 80386SX是32位架构但仅只有24位寻址(和16位资料总线)。除此之外其他架构都是32位 - 所有的寄存器、指令集、输出输入空间和存储器寻址。为了能够在后者所说的功能工作,要使用32位扩展的保护模式。然而不像286,386所有的区段可以使用32位的偏移量,即使存储器空间有使用区段,但也允许应用程序访问超过4GB空间而不需要区段的分隔。此外,32位保护模式提供标签页的支持,是一种让虚拟内存得以实现的机制。 没有新的通用寄存器被加入。所有16位的寄存器除了区段寄存器外都扩展为32位。Intel在寄存器的助记符号上加入“E”来表示(因此扩展的AX变成EAX,SI变成ESI,依此类推)。因为有更多的寄存器数量、指令、和运算单元,因此机器代码的格式也被扩展。为了提供与先前的架构兼容,包含运行码的区段可以被标示为16或是32位的指令集。此外,特殊的前置符号也可以用来在16位的区段包含32位的脚本,反之亦然。 标签页跟区段的存储器访问是为了支持现在多任务操作系统所必须要的。Linux、386BSD、Windows NT和Windows 95都是一开始为386所发展,因为它是第一颗提供可靠地程序分离存储器空间的支持(每个程序拥有自己的寻址空间)以及可以在必要的情况下打断他们程序的运行(使用ring,一种x86保护模式下权力分级的名称)。这种386的基本架构变成未来所有x86系列发展的基础。 Intel 80386数学辅助运算处理器也在集成到这个CPU之后的x86系列中,也就是Intel 80486。新的FPU可以帮助浮点数运算,对于科学计算和图形设计是非常重要。 生产商 目前仍在设计、生产并贩卖x86处理器的公司包括: 英特尔(Intel) 超微(AMD) 瞻营全电子(DMP Electronics INC.) 威盛电子(VIA) 兆芯 曾经设计、生产并贩卖x86处理器,但现已退出x86处理器市场的公司包括: Chips and Technologies Cyrix(被威盛电子收购) IBM IDT 国家半导体(NS,National Semiconductor) 日本电气(NEC) NexGen(被超微收购) Rise Technology(被硅统科技收购) 意法半导体 硅统科技(SiS) 德州仪器(TI,Texas Instruments) 全美达(Transmeta) 联华电子(UMC) Montalvo
  • [技术干货] 【技术长文】啥是单片机?
    单片机,全称单片微型计算机(英语:single-chip microcomputer),又称微控制器单元(microcontroller unit),是把中央处理器、存储器、定时/计数器(timer/counter)、各种输入输出接口等都集成在一块集成电路芯片上的微型计算机。与应用在个人电脑中的通用型微处理器相比,它更强调自供应(不用外接硬件)和节约成本。它的最大优点是体积小,可放在仪表内部,但存储量小,输入输出接口简单,功能较低。由于其发展非常迅速,旧的单片机的定义已不能满足,所以在很多应用场合被称为范围更广的微控制器;由于单片机微电脑常用于当控制器故又名single chip microcontroller。“单芯片”是台湾对单片机的称呼;中国大陆主要采用“单片机”的称呼,英文缩写为MCU。 概述 绝大多数现在的单片机都是基于冯·诺伊曼结构的,这种结构清楚地定义了嵌入式系统所必需的四个基本部分:一个中央处理器核心,程序存储器(只读存储器或者闪存)、数据存储器(随机存储器)、一个或者更多的定时/计数器,还有用来与外围设备以及扩展资源进行通信的输入/输出端口——所有这些都被集成在单个集成电路芯片上。 说单片机与通用型中央处理单元芯片不同,是因为前者一般很容易配合最小型的外部支持芯片制成工作计算机。这样就可以很容易的把单片机系统植入设备内部来控制设备了。近年来为了在指令和数据上使用不同的字宽,并提高处理器线速度,哈佛结构在微控制器(Microcontrollers)和数字信号处理器也逐渐得到了广泛的应用。 传统的微处理器是不允许这么做的。它要完成单片机的工作,就必须连接一些其他芯片。比如说,因为芯片上没有数据存储器,就必须要添加一些RAM的存储芯片,虽然所添加存储器的容量很灵活,但是至少还是要添加。另外还需要添加很多连线来传递芯片之间的数据。与以上的情况相比,单片机的工作则相对独立,一个典型的微控制器只需要一个时钟发生器和很少的RAM和ROM(或者EPROM, E2PROM)就可以在软件和晶振下工作了。同时,微控制器具有丰富的输入输出设备,例如模拟数字转换器、定时器、串口,以及其他串行通讯接口,比如I2C,串行周边接口,控制器局域网等。通常,这些集成在内部的设备可以通过特殊的指令来操作。 单片机时钟频率通常较同时代的计算机芯片低,但它价格低廉,能够提供充足的程序存储器、丰富的片上接口。某些架构的单片机生产厂商众多,例如8051系列、Z80系列。一些现代的微控制器支持一些内建的高级编程语言,比如BASIC(培基)语言、C语言、C++等。 单片机的比特数 根据总线或资料寄存器的宽度,单片机又分为4位、8位、16位和32位单片机。4位单片机多用于冰箱、洗衣机、微波炉等家电控制中;8位、16位单片机主要用于一般的控制领域,一般不使用操作系统;32位用于网络操作、多媒体处理等复杂处理的场合,一般要使用嵌入式操作系统。 常见的单片机 微芯(Microchip)的PIC系列出货量居于业界领导者地位;Atmel的51系列及AVR系列种类众多,受支持面广;德州仪器的MSP430系列以低功耗闻名,常用于医疗电子产品及仪器仪表中;瑞萨单片机在日本使用广泛。 WDC的W65C265S8PL单片机 英特尔 8-bit 8051系列 8XC42 MCS48 MCS51 : Intel 8051系列历史悠久,兼容产品众多,使用广泛 8xC251 16-bit MCS96系列 MXS296 32-bit i960 ARM (Acorn RISC Machine)系列单片机 两台Atmel牌的ATmega单片机 AVR系列(爱特梅尔公司) Atmel AT91 series (ARM 处理器等) AT90 series – AVR (Atmel 的高性能RISC 8位单片机,老产品) ATmega series – AVR (Atmel 的高性能RISC 8 位单片机,新产品) ATXmega series – AVR (Atmel 的高性能RISC 32 位单片机,新产品) Atmel AT89 series (Intel 8051/MCS51 架构8位单片机) MARC4 Cypress MicroSystems(赛普拉斯微系统公司) CY8C2xxxx (PSoC) 飞思卡尔半导体 8-bit 68HC05 (CPU05) 68HC08 (CPU08) 68HC11 (CPU11) 16-bit 68HC12 (CPU12) 68HC16 (CPU16) Freescale DSP56800 (DSPcontroller) 32-bit Freescale 683XX (CPU32) MPC500 MPC 860 (PowerQUICC) MPC 8240/8250 (PowerQUICC II) MPC 8540/8555/8560 (PowerQUICC III) Holtek(盛群半导体) HT48FXX Flash I/O type HT48RXX I/O type HT46RXX A/D type HT49RXX LCD type 微芯片科技(微芯公司)的PIC微控制器系列 8-bit : PIC10 PIC12 PIC16 PIC18 16-bit : PIC24F PIC24H dsPIC30 dsPIC33 32bit : PIC32 (采用MIPS M4K 核心架构) 美国国家半导体 COP8 CR16 NEC 78K 恩智浦半导体 LPC2000 LPC900 LPC700 Parallax, Inc. BASIC Stamp 瑞萨科技系列单片机 Renesas 16-bits Renesas M16C Series Renesas M32C Series Renesas R8C Series Renesas M16C/Tiny Series Renesas R8C/Tiny Series Renesas H8/Tiny Series 意法半导体 STM32 series (ARM Cortex-M3 系列,32位) STM8 series (自主RISC指令集,8 位) SyncMOS 新茂国际科技全系列单片机 SM59RXXA2 8-bits 1T(RISC) SM59DXXG2 8-bits 6T(ISP) SM59XX 8-bits 12T(ISP) SM89XX 8-bits 12T(Traditional 8051) SM79XX 8-bits 12T(Customization) PADAUK应广科技全系列单片机(多核心单片机) P201CS/CD 8-bits P211CS/CD 8-bits p232CS/CD 8-bits P234CS/CD 8-bits ZiLOG Z8 Z86E02 STC 宏晶系列单片机 STC89C series 6T/12T增强性单片机 (Intel 8051架构,有外部总线) STC11F/11L series 2T 增强性单片机 (Intel 8051增强架构,无外部总线程序支持) STC12C/12LE series 2T 增强性单片机 (Intel 8051增强架构,无外部总线程序支持) STC15C/15LE series 2T 增强性单片机 (Intel 8051增强架构,无外部总线程序支持,正在开发中) Kernel-IC 华芯单片机 LS系列 LSx051 series 12T 双核单片机(Intel 8051架构,无外部总线) LSx151 series 12T 三核单片机(Intel 8051架构,无外部总线) LS052A series 6T 三核单片机(Intel 8051架构,无外部总线) 新唐科技 Nuvoton NuMicro Family 8051 单片机 (8-bits) ARM Cortex-M0 单片机 (32-bits) ARM Cortex-M4 单片机 (32-bits) 其他系列的单片机 MSP430系列单片机 LM3S系列单片机 北京君正 JZ系列单片机 8098、80196系列单片机 AT8P5X系列单片机 CZG8000系列单片机 单片机的开发 单片机的软件开发中,以往多使用汇编语言,如今越来越多的使用C语言,又或者使用BASIC语言等更适合初学者的语言,部分集成开发环境支持C++。单片机的软件测试需要使用单片机开发器或模拟器。 随着技术的发展,2000年后已经有很多单片机自带了ISP(在线编程设计)或支持IAP,彻底地改变了传统的开发模式,使得开发单片机系统时不会损坏芯片的引脚,加速了产品的上市并降低了研发成本,缩短了从设计、制造到现场调试的时间,简化了生产流程,大大提高了工作效率。这类单片机包括AT89S系列单片机、AVR系列单片机等。 硬件 开发板可以进一步简化程序的开发和烧制过程。开发板可能包含实际芯片或是仿真器。通过配套的下载线连接电脑与开发板,在电脑上编写程序下载到开发板。批量生产会用到编程器。 软件开发环境 一些知名的微控制器开发环境有: 微芯公司的 MPLAB IDE 可用于该公司全系列微控制器的开发与调试,除支持该公司的 ASM汇编器 C语言编译器外,也支持许多第三方的编译器。 ARM公司的 μVision 集成开发环境(原为 Keil 公司产品,2005年该公司已由 ARM 购并),包含汇编器与 C编译器,可用于51单片机及ARM开发。 PROTEUS Proteus软件是初学者入门的首选软件,它是Labcenter Electronics公司的一款商业版电路设计与仿真软件。内包括ISIS、ARES等软件模块。ARES模块主要用来完成PCB的设计,ISIS模块用来完成电路原理图的布图与仿真。 Proteus的软件仿真基于VSM技术,它与其他软件最大的不同也是最大的优势就在于能仿真大量的单片机芯片,比如MCS-51系列、PIC系列等等,甚至ARM处理器,以及单片机外围电路,比如键盘、LED、LCD等等。该软件还附带了一些案例和丰富的帮助文件。
  • [问题求助] nblot模块如何移植到其他单片机上
    nblot模块如何移植到其他单片机上
  • [技术探讨] 小熊派的nblot 如何移植到其他的单片机上 例如树莓派
    小熊派的nblot 如何移植到其他的单片机上 例如树莓派想用nblot进行两个树莓派之间的远程通信
  • [技术干货] 华为云学院-人人学loT学习笔记- 第三章 窄带无线,宽带互联
    3.1 NB-loT课程大纲3.1 NB-loT低功耗广域网技术(LPWA)包括:Halow,SigFox,NB-IoT,RPMA,LoRa其中以NB-IoT最为人关注。其研究及标准化工作是3GPP标准组织进行的。目前全球多数运营商选择NB-IoT作为蜂窝物联网演进的第一步。1,NB-IoT网络总架构NB-IoT终端:感知层;负责数据信息的采集处理;通过通信模组无线连接,发送数据。eNodeB基站(EPC核心网MME,HSS,PGW,SGW):网络层;负责数据信息的接入,传输和转发。(eNodeB基站是低成本站点解决方案,支持更大容量的连接,通过原有的2G或4G基站上通过软件或通过基带板进行升级,并且相比之前支持更大容量的连接)2,NB-IoT核心网包括四个核心网源。这四个网源也是LTE的网源。MME(Mobility Management Entity)移动性管理实体。是LTE接入网络的控制节点。负责空闲模式终端设备的定位。传呼过程包括中继,简单讲MME是负责信令处理部分。HSS(Home Subscriber Server)归属用户服务器,用户归属网络中存储用户信息的核心数据库,主要用于在归属网络中保存用户的签约信息。SGW(Service Gateway)服务网关,负责和无线建立连接,把用户的数据包转发至PGW.PGW,PDN(Packet Data Network)Gateway,PDN网关,是3GPP与non-3GPP数据间的用户面数据链路的锚点。主要负责管理3GPP和non-3GPP间的数据路由,策略执行,计费等功能。NB-IoT的核心网承载了终端的安全接入,连接管理,流量调度,网络鉴权,流量计费等功能。物联网平台属于平台层,主要负责:数据存储和管理;应用层协议栈的适配,终端设备管理;API能力开发,大数据分析。第三方应用属于应用层,主要负责数据呈现与用户界面的交互。比如APP软件,WEB界面。3.2 NB-IoT中的Niubility技术1,NB-IoT最底层是物理层,系统宽带为180KHZ,上行技术是SC-FDMA(Single-carrier Frequency-Division Multiple Access 单载波频分多址)(子载波连续的调制解调技术,LTE上行链路的主流技术)通常上行传输有单载波与多载波两种传输技术。单载波(Single-tone)表示终端设备上行数据传输仅占用一个子载波。多载波(Multi-tone)表示可以占用多个子载波进行上行数据传输。在相同功率的前提下,单载波拥有更高的功率谱密度增益。NB-IoT上行支持单载波和多载波传输,单载波作为终端设备的必备功能,而多载波为可选功能。NB-IoT下行技术采用OFDMA,OFDMA是OFDM技术的演进。是OFDM和FDMA技术结合。OFDMA(Orthogonal Frequency Division Multiple Access正交频分多址)。NB-IoT利用OFDMA将180KHZ带宽分成12个子载波,每个子载波15KHZ.对比LTE,实现更简单,且精简了不必要的物理信道。2,NB-IoT上行有两种物理信道,一种参考信号。下行有三种物理信道,两种参考信号。措施:降低目标速率,多次传输,采用低阶调制方式。目的:增加覆盖,降低成本,降低功耗。3,NB-IoT支持三种部署方式:独立部署(Stand-alone)可以利用单独的频带,适合用于GSM的频段的重耕。保护带部署(Guard-band)可以利用LTE系统中边缘无用频带。带内部署(Inband)可以利用LTE载波中间的任何资源模块。4,四大关键特性:超低成本基于华为的SingleRAN解决方案,支持在现有网络上进行升级改造,从而降低网络建设和维护成本。且NB-IoT芯片是专为互联网设备设计,只针对窄带,低速率。并针对物联网需求,只支持单天线,半双工方式。简化信令处理,降低芯片价格。超低功耗物联网与手机信号不同,其只会上行发送数据包,且由自身决定是否发送。不需其他终端的呼叫。小包,偶发的物联网应用场景,NB-IoT设计两种独特的省电模式PSM(Power Save Model)与eDRX(Extend Discontinuous Reception扩展非连续接收).其中PSM最为省电。对应的还有DRX(非连续接收),周期为1.28s.eDRX周期最大至2.92h.相比PSM,增加了下行可达性。NB-IoT可分别独立使用两种省电模式。PSM应用于智能抄表。eDRX应用于共享单车,物流跟踪。也可同时使用。当PSM激活期>eDRX周期时终端设备可以进入eDRX周期,降低功率。超强覆盖通过时域重传技术(即重复发送)和提升功率谱密度比GSM多增加20dB MCL(最大耦合损耗)LTE MCL=142.7GSM MCL=144NB-IoT MCL=164NB-IoT是GSM三倍覆盖距离,比后者多穿透两堵墙。MCL(Max Coupling Loss)最大耦合损耗可以衡量覆盖范围,数值越大覆盖范围越大。功率谱密度提升:15KHZ子载波数据传输相比180KHZ带宽传输,可以提升11dB增益。引入重复发送的编码方式,通过重复发送,提升信道条件恶劣时的传输可靠性。提升9dB的下行增益和12dB的上行增益。超大连接更少站点可以覆盖更广区域和具备更强穿透性。可穿透楼层到地下室。将隐蔽位置的设备入水表以及要求广覆盖的宠物跟踪等业务得到应用。物联网话务模型特点:终端多,发送的包小,时延要求不敏感。给予其特点,可以设计更多的用户接入,保存更多的用户上下文。可以让100K的终端同时在一个小区。5,关键技术上行业务调度单元较小,资源利用率更高。PSM/eDRX降低设备对基站的资源使用。减小空口信令开销。基站,核心网优化。3.3 excellent技术目前工业物联网主流趋势是自建工业物联网专网,实现园区产业升级。eLTE-IoT解决方案是华为专为行业物联市场开发的基于3GPP标准的窄带无线物联解决方案。eLTE-IoT:1GHz以下的非授权ISM频谱;灵活易部署的轻量化设备;支持标准物联网协议与企业现有应用平台进行对接。目标市场为企业自建窄带物联应用市场,还可以应用在制造,电力,水务,智慧城市等。特性:基于ISM频谱可靠连接;大容量海量物联;最长10年更低功耗;最大10KM更广覆盖。可以支持三种频段:中国470510MHz;美洲902928MHz;欧洲863~870MHz.采用跳频技术避免外部干扰提高可靠性。用强大的收发机制保证可靠连接。eLTE-IoT的网源设备包括:业务引擎(核心汇聚节点,终端设备管理;终端和AirNode接入认证鉴权;网络协议处理;数据转发;与第三方平台应用对接)IoT一体化基站AirNode(轻量化设计的基站,支持挂墙和抱杆安装,支持有线传输(光纤/网线)与业务引擎直连,也可通过外置3G/4G无线回传模块,无线网络回传。用户接入终端CPEIoT模组和网管设备————————————————原文链接:https://blog.csdn.net/qq_41952762/article/details/108762189
  • [技术干货] 华为初识IoT
    1.1初探物联网一,物联网百度解释:物联网(The Internet of Things,简称IOT)是指通过 各种信息传感器、射频识别技术、全球定位系统、红外感应器、激光扫描器等各种装置与技术(可理解为通信感知技术),实时采集任何需要监控、 连接、互动的物体或过程,采集其声、光、热、电、力学、化 学、生物、位置等各种需要的信息,通过各类可能的网络接入,实现物与物、物与人的泛在连接,实现对物品和过程的智能化感知、识别和管理。物联网是一个基于互联网、传统电信网等的信息承载体,它让所有能够被独立寻址的普通物理对象形成互联互通的网络。物联网( IoT ,Internet of things )即“万物相连的互联网”,是互联网基础上的延伸和扩展的网络,将各种信息传感设备与互联网结合起来而形成的一个巨大网络,实现在任何时间、任何地点,人、机、物的互联互通。其掀起了世界信息产业发展的第三次浪潮(前两次是计算机和互联网)。通信感知技术是物联网实现的基础。二,物联网发展历程:1991年伦敦大学 特洛伊咖啡壶服务器1999年 Kevin Ashton在MIT首次提出物联网概念早期概念:依托RFID(射频识别)技术和设备按约定胡通信协议与互联网结合,使物品信息实现智能化识别和管理,实现物品信息互联,可交换和共享而形成胡网络。2005,7 国际电信联盟引用物联网概念(技术范围扩大)2008,11 IBM提出“智慧地球”2009,8 “感知中国”无锡首先建立"感知中国"研究中心2013,4 德国汉诺威工业博览会上提出“工业4.0”,旨在提升制造业的智能化水平2015,5 “中国制造2025”发布三,发展历程总结并划分为三个时期:1,概念期(1999~2013)物联网概念的形成和提出时期(基本处于概念阶段),物联网涉及到的“物”,仅仅有无线射频设备。2,发展期(2014~2016)以Google收购Nest为标志,物联网从无线射频设备进化到了智能可穿戴和智能家居设备。3,成熟期(2016至今)智能可穿戴设备和智能家居设备已经成熟落地并且切实进入到了日常生活当中,与此同时物联网平台技术的成熟度正在快速爬升,为物联网生态系统的建设打下了良好的基础。四,物联网层次划分云—管—端的逻辑体系来划分,物联网的层次为四个层次应用层:负责数据呈现及客户交互平台层:负责设备通信管理,数据存储,业务规划等网络层:负责终端接入和数据传输感知层:负责信息收集和信号处理从垂直行业应用来划分,物联网的行业应用可以简单分成几大领域公共事业物联网工业物联网车联网智慧家庭物联网等五,各种技术特点及其应用4G,5G,LTE-V : 传输速率>10Mbps,功耗较高,成本较高 应用于车联网,视频监控,智能机器等eMTC,GPRS : 传输速率约为1Mbps ,成本较低,功耗较低 应用于智能穿戴设备,梯联网,电子广告,无线ATM等NB-loT : 传输速率<100kbps ,成本低,功耗低,覆盖广 应用于远程抄表,智能停车,智慧农业等所有技术都可以分为有线通信技术和无线通信技术。知识扩展LTE-V是专门针对车间通讯的协议,被称为是影响车联网“连接”的起始点。目前的LTE-V版本属于4.5G技术,未来可以平滑演进到5GLTE-V其实是实现V2X(Vehicle to Everything)的两大技术阵营之一,它主要由国内企业(包括大唐、华为等)推动,另一大阵营是美国主导的IEEE 802.11P(DSRC)。1.2 有线通信有线通信技术特点是稳定可靠。缺点是受限于传输媒介,满足不了一些远距离的、灵活性强的连接。包括:以太网,RS-232,RS-485,M-Bus,PLC等以太网是现有局域网采用的最通用的通信协议标准,包括:标准的以太网、快速以太网、10G以太网。例如我们通常用的家庭路由器宽带技术就有涉及以太网技术。RS-232是EIA制定的异步传输标准接口,个人计算机上的通讯接口之一,9个引脚的是DB-9,25引脚的DB-25,这两类通常标识为COM1和COM2.用于监视和控制系统的应用。RS-232与RS485比较RS232 采用不平衡传输方式,单端通讯。传输距离不超过20m,一对一通信RS-485 采用平衡传输,差分传输方式。传输距离几十米到上千米,总线上允许连接128个收发器。M-Bus-Meter Bus-户用仪表总线:用于非电力户用仪表传输的欧洲总线标准。专门为消耗测量仪器和计数器传送信息的数据总线设计。M-Bus在建筑物和工业能源消耗数据采集有多方面的应用M-Bus总线的概念基于OSI参考模型,但是M-Bus又不是真正意义上的一种网络。其仅在物理层,链路层,网络层和应用层进行了功能定义(OSI另外三层为传输层,会话层和表示层)。由于OSI参考模型中不允许上一层次改变如波特率、地址等参数,因此在七层模型之外M-Bus定义了一个管理层,可以不遵守OSI模型对任意一层次进行管理。M-Bus总线的提出满足了公用事业仪表的组网和远程抄表的需要。同时,它还可以满足远程供电或电池供电系统的特殊要求(距离达1千米,24V供电)。M-Bus串行通信方式的总线型拓扑结构,非常适合公用事业仪表的可靠、低成本的组网要求,可以在几公里的距离上连接几百个从设备。PLC全称Power Line Communication,也称 电力线通信PLC是利用电力线传输数据和媒体信号的一种通信方式。百度解释PLC多意词。其另一个意思是可编程逻辑控制器。电力线通信技术是把载有信息的高频加载于电流然后用电线传输接受信息的适配器再把高频从电流中分离出来并传送到计算机或电话以实现信息传递。电力线通信全称是电力线载波(Power Line Carrier – PLC)通信,是指利用高压电力线(在电力载波领域通常指35kV及以上电压等级)、中压电力线(指10kV电压等级)或低压配电线(380/220V用户线)作为信息传输媒介进行语音或数据传输的一种特殊通信方式。PLC电力线通信应用形式多种多样,除了我们熟知的,可以通过电力线通信将电表的数据传输到工业网关上还可以作为家庭网络,PLC非常便于在传统数据处理设备(如PC机等)与计算机外设之间交换数据。此外,信息家电也可与计算机进行对话,利用PLC可以很方便地从电视机或VCR向PC机发送多媒体数据。PLC还可以用于家庭安全方面,可以把门口监控摄像机获得的图像送至电视机。1.3 无线通信蜂窝移动通信----------2G、3G、4G通信技术目前,较多的物联网终端设备接入是使用GPRS通信方式,也就是常说的2G中的一种技术。像较多共享单车的网络接入,POS机的网络接入,都是通过GPRS。短距无线通信技术----------蓝牙、WIFI、ZigBee、Z-Wave蓝牙:大容量、近距离、无线数字通信技术标准 最高传输速率1Mbps 最大传输距离为10厘米到10米的数据传输(可扩展到100米) 速率块、低功耗、安全性高、网络节点少,不适合多点布控。可实现固定设备,移动设备,楼宇个人域网之间的短距离数据交换。主要使用2.4~2.485GHz的ISM波段无线电波。最初由电信巨头爱立信公司创制,应用有手机,蓝牙耳机,蓝牙音箱,智能穿戴,家电设备等。WIFI:一种允许电子设备连接到一个无线局域网(WLAN)的技术,通常使用2.4G UHF或5G SHF ISM射频频段 覆盖范围广,数据传输速率块 传输安全性不好,稳定性差,功耗略高。ZigBee:是基于IEEE802.15.4标准的低功耗局域网协议 广泛应用于工业、家庭领域。优点: 近距离,低复杂度、自组织、低功耗、低数据速率 缺点:物体阻挡后信号会衰减、不同芯片兼容性较差,网络较灵活,不易维护百度解释ZigBee,也称紫蜂,是一种低速短距离传输的无线网上协议,底层是采用IEEE 802.15.4标准规范的媒体访问层与物理层。主要特色有低速、低耗电、低成本、支持大量网上节点、支持多种网上拓扑、低复杂度、快速、可靠、安全。主要应用于工业和智慧家庭领域。ZigBee是一项新型的无线通信技术,适用于传输范围短数据传输速率低的一系列电子元器件设备之间。 ZigBee无线通信技术可于数以千计的微小传感器相互间,依托专门的无线电标准达成相互协调通信,因而该项技术常被称为Home RF Lite无线技术、FireFly无线技术。Z-Wave:一种新兴的基于射频的、低成本、低功耗、高可靠的短距离无线通信技术 用于住宅、照明商业控制、状态读取应用(抄表、照明灯)网络结构简单、低功耗、低成本 速率较低,标准不开放,芯片只能通过Sigma Designs这唯一来源获取。Z-Wave技术设计用于住宅、照明、商业控制以及状态读取应用,例如抄表、照明、家电控制、接入控制、防盗及火灾监测等。信号的有效覆盖范围在室内是30m,室外可超过100m,适合于窄宽带应用场合。百度解释Z-Wave是由丹麦公司Zensys所一手主导的无线组网规格,Z-wave联盟(Z-wave Alliance)虽然没有ZigBee联盟强大,但是Z-wave联盟的成员均是已经在智能家居领域有现行产品的厂商,该联盟已经具有160多家国际知名公司下图为各种技术对比1.4 LPWA(Low Power Wide Area)低功耗广域网传统无线通信技术包含了蜂窝移动通信和短距无线通信技术这两类其他发展出的技术:无线通信技术LPWA低功耗广域网,它解决了传统网络不能应对一些物联网场景的通信问题,低功耗广域网的通信技术,其中比较被世人熟知的是SigFox、LoRa、NB-loT。1,SigFox这一通信技术由法国的 SigFox公司拥有,其主要打造低功耗、低成本的无线物联网专用网络。是商用化速度较快的一个LPWA网络技术。SigFox网络利用了UNB技术,传输功耗水平非常低,并仍然能维持一个稳定的数据连接,通常它的传输速率只有100bps。其网络拓扑是一个可扩展的、高容量的网络,具有非常低的能源消耗,同时保持简单和易于部署的基于星型单元的基础设施。SigFox无线链路使用免授权Sub-G的ISM射频频段,频率根据国家法规有所不同(欧洲:868MHz,美国:915MHz)。,2,LoRa:美国SEMTECH公司基于开源的MAC层协议的低功耗广域网标准,同时基于Sub-GHz的频段使其更易以较低功耗远距离通信 基于扩频技术的超远距离无线传输方案3,NB-loT是目前LPWA领域最火的一项技术。NB-loT是构建于蜂窝网络的窄带物联网,只消耗大约180KHz的宽带。可直接部署与GSM网络、UMTS网络、LTE网络。三项技术对比:NB-loT构建在运营商蜂窝网络上,其他两项不是;三者都是基于Sub-GHz频段;NB-loT需要授权,其他两项不需要。知识扩展NB-IoT是一种无线蜂窝网络通信协议,是NArrow Band Internet of Things的缩写,意思是窄带物联网,是一种低功耗广覆盖物联网技术(LPWA),窄带只指使用的带宽为180KHz,工作在运营商的授权频段内,技术主要贡献者为华为和高通。NB-IoT(Narrow Band Internet of Things,窄带蜂窝物联网)是基于LTE的R13实现NB-IoT协议就是充分利用好宝贵的无线频谱资源和时间资源、满足LPWA需要而设计的一套无线蜂窝网络通信规则,其作用就是为信息交互制定了一套语法、语义和时序,简单的说就是规定了通信参与方用什么语言交流,说的话代表什么意思、以及谁说谁听、说多长时间的规则。NB-IoT的演进过程2013年初,华为联合相关厂商、运营商开始蜂窝物联网的研究,命名为LTE-M;2014年5月,由华为、沃达丰、中国移动、Orange、TelecoMItaly等公司主导的工作组SI在3GPPGERAN立项,LTE-M演进为CellularIoT,简称CIoT;2015年5月,华为和高通共同宣布一项融合解决方案,融合之后的方案叫NB-CIoT;2015年8月,在GERANSI阶段最后一次会议上,爱立信联合中兴、诺基亚、三星、INTEL等提出NB-LTE的概念;2015年9月,RAN#68上NB-CIoT和NB-LTE两个技术方案进行融合行成NB-IoT;2016年6月16日,NB-IoT的3GPP标准核心部分正式冻结,标志着商用阶段正式开始。2016年是物联网生态元年————————————————原文链接:https://blog.csdn.net/qq_41952762/article/details/108691464
总条数:248 到第
上滑加载中