单片机

*实例1:使用P3口流水点亮8位LED

#include//包含单片机寄存器的头文件 / 函数功能:延时一段时间

void delay(void)
{
unsigned char i,j;
for(i=0;i<250;i++)
for(j=0;j<250;j++)
;
}
/ 函数功能:主函数
void main(void)
{
while(1)
{
P3=0xfe; //第一个灯亮
delay(); //调用延时函数
P3=0xfd; //第二个灯亮
delay(); //调用延时函数
P3=0xfb; //第三个灯亮
delay(); //调用延时函数
P3=0xf7; //第四个灯亮
delay(); //调用延时函数
P3=0xef; //第五个灯亮
delay(); //调用延时函数
P3=0xdf; //第六个灯亮
delay(); //调用延时函数
P3=0xbf; //第七个灯亮
delay(); //调用延时函数
P3=0x7f; //第八个灯亮
delay(); //调用延时函数
} / /
}

*实例2:通过对P3口地址的操作流水点亮8位LED

#include//包含单片机寄存器的头文件

sfr x=0xb0; //P3口在存储器中的地址是b0H, 通过sfr可定义8051内核单片机
//的所有内部8位特殊功能寄存器,对地址x的操作也就是对P1口的操作
/
函数功能:延时一段时间
/
void delay(void)
{
unsigned char i,j;
for(i=0;i<250;i++)
for(j=0;j<250;j++)
; //利用循环等待若干机器周期,从而延时一段时间
}
/
函数功能:主函数
/
void main(void)
{
while(1)
{
x=0xfe; //第一个灯亮
delay(); //调用延时函数
x=0xfd; //第二个灯亮
delay(); //调用延时函数
x=0xfb; //第三个灯亮
delay(); //调用延时函数
x=0xf7; //第四个灯亮
delay(); //调用延时函数
x=0xef; //第五个灯亮
delay(); //调用延时函数
x=0xdf; //第六个灯亮
delay(); //调用延时函数
x=0xbf; //第七个灯亮
delay(); //调用延时函数
x=0x7f; //第八个灯亮
delay(); //调用延时函数
}
}

*实例3:用不同数据类型控制灯闪烁时间

#include//包含单片机寄存器的头文件

/
函数功能:用整形数据延时一段时间
/
void int_delay(void) //延时一段较长的时间
{
unsigned int m; //定义无符号整形变量,双字节数据,值域为0~65535 for(m=0;m<36000;m++)
; //空操作
}
/
函数功能:用字符型数据延时一段时间
/
void char_delay(void) //延时一段较短的时间
{
unsigned char i,j; //定义无符号字符型变量,单字节数据,值域0~255 for(i=0;i<200;i++)
for(j=0;j<180;j++)
; //空操作
}
/
函数功能:主函数
/
void main(void)
{
unsigned char i;
while(1)
{
for(i=0;i<3;i++)
{
P1=0xfe; //P1.0口的灯点亮
int_delay(); //延时一段较长的时间
P1=0xff; //熄灭
int_delay(); //延时一段较长的时间
}
for(i=0;i<3;i++)
{
P1=0xef; //P1.4口的灯点亮
char_delay(); //延时一段较长的时间
} P1=0xff; //熄灭 char_delay(); //延时一段较长的时间 } }

*实例4:用单片机控制第一个灯亮

#include//包含51单片机寄存器定义的头文件

void main(void)
{
P1=0xfe; //P1=1111 1110B,即P1.0输出低电平
}

*实例5:用单片机控制一个灯闪烁:认识单片机的工作频率 #include//包含单片机寄存器的头文件

*实例6:将 P1口状态分别送入P0、P2、P3口:认识I/O口的引脚功能

#include//包含单片机寄存器的头文件
/ 函数功能:主函数 (C语言规定必须有也只能有1个主函数) /
void main(void)
{
while(1) //无限循环
{
P1=0xff; // P1=1111 1111B,熄灭LED
P0=P1; // 将 P1口状态送入P0口
P2=P1; // 将 P1口状态送入P2口
P3=P1; // 将 P1口状态送入P3口
}
}

*实例7:用P0口、P1 口分别显示加法和减法运算结果 

#include

void main(void)
{
unsigned char m,n;
m=43; //即十进制数2x16+11=43
n=60; //即十进制数3x16+12=60
P1=m+n; //P1=103=0110 0111B,结果P1.3、P1.4、P1.7 口的灯被点亮 P0=n-m; //P0=17=0001 0001B,结果P0.0、P0.4的灯被熄灭 }

*实例8:用P0、P1口显示乘法运算结果

#include//包含单片机寄存器的头文件

void main(void)
{
unsigned char m,n;
unsigned int s;
m=64;
n=71;
s=m n; //s=64 71=4544,需要16位二进制数表示,高8位送P1口,低8位送P0口
//由于4544=17 256+192=H3 16 16 16+H2 16 16+H1 16+H0
//两边同除以256,可得17+192/256=H3 16+H2+(H1 16+H0)/256
//因此,高8位16进制数H3 16+H2必然等于17,即4544除以256的商
//低8位16进制数H1 16+H0必然等于192,即4544除以256的余数
P1=s/256; //高8位送P1口 ,P1=17=11H=0001 0001B, P1.0和P1.4口灭,其余亮
P0=s%256; //低8位送P0口 , P3=192=c0H=1100 0000B,P3.1,P3.6,P3.7口灭,其余亮
}

*实例9:用P1、P0口显示除法运算结果

#include//包含单片机寄存器的头文件

void main(void)
{
P1=36/5; //求整数
P0=((36%5) 10)/5; //求小数
while(1)
; //无限循环防止程序“跑飞”

}

*实例10:用自增运算控制P0口8位LED流水花样

#include//包含单片机寄存器的头文件

/
函数功能:延时一段时间
/
void delay(void)
{
unsigned int i;
for(i=0;i<20000;i++)
;
}
/ 函数功能?:主函数
/ void main(void)
{
unsigned char i;
for(i=0;i<255;i++) //注意i的值不能超过255
{
P0=i; //将i的值送P0口
delay(); //调用延时函数
}

}

*实例11:用P0口显示逻辑"与"运算结果 #include//包含单片机寄存器的头文件

void main(void)

{
P0=(4>0)&&(9>0xab);//将逻辑运算结果送P0口
while(1)
; //设置无限循环,防止程序“跑飞”
}

*实例12:用P0口显示条件运算结果

#include//包含单片机寄存器的头文件 void main(void)

{
P0=(8>4)?8:4;//将条件运算结果送P0口,P0=8=0000 1000B while(1)
; //设置无限循环,防止程序“跑飞”
}

*实例13:用P0口显示按位"异或"运算结果 

#include//包含单片机寄存器的头文件

void main(void)

{
P0=0xa2^0x3c;//将条件运算结果送P0口,P0=8=0000 1000B while(1)
; //设置无限循环,防止程序“跑飞”
}

*实例14:用P0显示左移运算结果

#include//包含单片机寄存器的头文件 void main(void)

{
P0=0x3b<<2;//将左移运算结果送P0口,P0=1110 1100B=0xec while(1)
; //无限循环,防止程序“跑飞”
}
*实例15:"万能逻辑电路"实验

#include//包含单片机寄存器的头文件

sbit F=P1^4; //将F位定义为 P1.4
sbit X=P1^5; //将X位定义为 P1.5
sbit Y=P1^6; //将Y位定义为 P1.6
sbit Z=P1^7; //将Z位定义为 P1.7
void main(void)
{
while(1)
{
F=((~X)&Y)|Z; //将逻辑运算结果赋给F
;
}
}

*实例16:用右移运算流水点亮P1口8位LED 

#include//包含单片机寄存器的头文件

/
函数功能:延时一段时间
/
void delay(void)
{
unsigned int n;
for(n=0;n<30000;n++)
;
}
/
函数功能:主函数
/
void main(void)
{
unsigned char i;
while(1)
{
P1=0xff;
delay();
for(i=0;i<8;i++)//设置循环次数为8
{
P1=P1>>1; //每次循环P1的各二进位右移1位,高位补0 delay(); //调用延时函数
}
}
}

*实例17:用if语句控制P0口8位LED的流水方向

 #include//包含单片机寄存器的头文件

sbit S1=P1^4; //将S1位定义为P1.4
sbit S2=P1^5; //将S2位定义为P1.5
/
函数功能:主函数
/
void main(void)
{
while(1)
}
{ if(S1==0) //如果按键S1按下 P0=0x0f; //P0口高四位LED点亮 if(S2==0) //如果按键S2按下 P0=0xf0; //P0口低四位LED点亮 }

*实例18:用swtich语句的控制P0口8位LED的点亮状态 

#include//包含单片机寄存器的头文件

sbit S1=P1^4; //将S1位定义为P1.4
/
函数功能:延时一段时间
/
void delay(void)
{
unsigned int n;
for(n=0;n<10000;n++)
;
}
/
函数功能:主函数
/
void main(void)
{
unsigned char i;
i=0; //将i初始化为0
while(1)
{
if(S1==0) //如果S1键按下
{
delay(); //延时一段时间
if(S1==0) //如果再次检测到S1键按下
i++; //i自增1
if(i==9) //如果i=9,重新将其置为1
i=1;
}
switch(i) //使用多分支选择语句
{
}
} case 1: P0=0xfe; //第一个LED亮 break; case 2: P0=0xfd; //第二个LED亮 break; case 3:P0=0xfb; //第三个LED亮 break; case 4:P0=0xf7; //第四个LED亮 break; case 5:P0=0xef; //第五个LED亮 break; case 6:P0=0xdf; //第六个LED亮 break; case 7:P0=0xbf; //第七个LED亮 break; case 8:P0=0x7f; //第八个LED亮 break; default: //缺省值,关闭所有LED P0=0xff; }

*实例19:用for语句控制蜂鸣器鸣笛次数

#include//包含单片机寄存器的头文件 sbit sound=P3^7; //将sound位定义为P3.7 / 函数功能:延时形成1600Hz音频

/ void delay1600(void)
{
unsigned char n;
for(n=0;n<100;n++)
;
}
/ 函数功能:延时形成800Hz音频
/ void delay800(void)
{
unsigned char n;
for(n=0;n<200;n++)
;
}
/ 函数功能:主函数
/ void main(void)
{
unsigned int i;
while(1)
{
for(i=0;i<830;i++)
{
sound=0; //P3.7输出低电平 delay1600();
sound=1; //P3.7输出高电平 delay1600();
}
for(i=0;i<200;i++)
{
sound=0; //P3.7输出低电平 delay800();
sound=1; //P3.7输出高电平 delay800();
}
}
}

*实例20:用while语句控制LED

#include//包含单片机寄存器的头文件 / 函数功能:延时约60ms (3 100 200=60000μs) / void delay60ms(void)

{
unsigned char m,n;
for(m=0;m<100;m++)
for(n=0;n<200;n++)
;
}
/
函数功能:主函数
/
void main(void)
{
unsigned char i;
while(1) //无限循环
{
i=0; //将i初始化为0
while(i<0xff) //当i小于0xff(255)时执行循环体 {
P0=i; //将i送P0口显示
delay60ms(); //延时
i++; //i自增1
}
}
}

*实例21:用do-while语句控制P0口8位LED流水点亮 

#include//包含单片机寄存器的头文件

/
函数功能:延时约60ms (3 100 200=60000μs)
/
void delay60ms(void)
{
unsigned char m,n;
for(m=0;m<100;m++)
for(n=0;n<200;n++)
;
}
/
函数功能:主函数
/
void main(void)
{
do
{
P0=0xfe; //第一个LED亮
delay60ms();
}
P0=0xfd; //第二个LED亮 delay60ms(); P0=0xfb; //第三个LED亮 delay60ms(); P0=0xf7; //第四个LED亮 delay60ms(); P0=0xef; //第五个LED亮 delay60ms(); P0=0xdf; //第六个LED亮 delay60ms(); delay60ms(); P0=0xbf; //第七个LED亮 delay60ms(); P0=0x7f; //第八个LED亮 delay60ms(); }while(1); //无限循环,使8位LED循环流水点亮

*实例22:用字符型数组控制P0口8位LED流水点亮 

#include//包含单片机寄存器的头文件

/
函数功能:延时约60ms (3 100 200=60000μs)
/
void delay60ms(void)
{
unsigned char m,n;
for(m=0;m<100;m++)
for(n=0;n<200;n++)
;
}
/
函数功能:主函数
/
void main(void)
{
unsigned char i;
unsigned char code Tab[ ]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f}; //定义无符号字符型数组
while(1)
{
for(i=0;i<8;i++)
{
P0=Tab[i];//依次引用数组元素,并将其送P0口显示
delay60ms();//调用延时函数
}
}
}

*实例23:用P0口显示字符串常量

#include//包含单片机寄存器的头文件

/
函数功能:延时约150ms (3 200 250=150 000μs=150ms
/
void delay150ms(void)
{
unsigned char m,n;
for(m=0;m<200;m++)
for(n=0;n<250;n++)
;
}
/
函数功能:主函数
/
void main(void)
{
unsigned char str[]={"Now,Temperature is :"}; //将字符串赋给字符型全部元素赋值
unsigned char i;
while(1)
{
i=0; //将i初始化为0,从第一个元素开始显示
while(str[i]!='\0') //只要没有显示到结束标志'\0'
{
P0=str[i]; //将第i个字符送到P0口显示
delay150ms(); //调用150ms延时函数
i++; //指向下一个待显字符
}
}
}

*实例24:用P0 口显示指针运算结果

#include

void main(void)
{
unsigned char p1, p2; //定义无符号字符型指针变量p1,p2 unsigned char i,j; //定义无符号字符型数据
i=25; //给i赋初值25
j=15;
p1=&i; //使指针变量指向i ,对指针初始化
p2=&j; //使指针变量指向j ,对指针初始化
P0= p1+ p2; // p1+ p2相当于i+j,所以P0=25+15=40=0x28
//则P0=0010 1000B,结果P0.3、P0.5引脚LED熄灭,其余点亮 while(1)
; //无限循环,防止程序“跑飞”
}

*实例25:用指针数组控制P0口8位LED流水点亮 

#include

/
函数功能:延时约150ms (3 200 250=150 000μs=150ms
/
void delay150ms(void)
{
unsigned char m,n;
for(m=0;m<200;m++)
for(n=0;n<250;n++)
;
}
/
函数功能:主函数
/
void main(void)
{
unsigned char code Tab[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
unsigned char p[ ]={&Tab[0],&Tab[1],&Tab[2],&Tab[3],&Tab[4],&Tab[5], &Tab[6],&Tab[7]};
unsigned char i; //定义无符号字符型数据
while(1)
{
for(i=0;i<8;i++)
{
P0= p[i];
delay150ms();
}
}
}

*实例26:用数组的指针控制P0 口8 位LED流水点亮

 #include

/
函数功能:延时约150ms (3 200 250=150 000μs=150ms
/
void delay150ms(void)
{
unsigned char m,n;
for(m=0;m<200;m++)
for(n=0;n<250;n++)
;
}
/
函数功能:主函数
/
void main(void)
{
unsigned char i;
unsigned char Tab[ ]={0xFF,0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF, 0x7F,0xBF,0xDF,0xEF,0xF7,0xFB,0xFD,0xFE, 0xFE,0xFC,0xFB,0xF0,0xE0,0xC0,0x80,0x00, 0xE7,0xDB,0xBD,0x7E,0x3C,0x18,0x00,0x81, 0xC3,0xE7,0x7E,0xBD,0xDB,0xE7,0xBD,0xDB}; //流水灯控制码
unsigned char p; //定义无符号字符型指针
p=Tab; //将数组首地址存入指针p
while(1)
{
for(i=0;i<32;i++) //共32个流水灯控制码
{
P0= (p+i); // (p+i)的值等于a[i]
}
delay150ms(); //调用150ms延时函数 } }

*实例27:用P0 、P1口显示整型函数返回值 

#include

/ 函数功能:计算两个无符号整数的和
/ unsigned int sum(int a,int b)
{
unsigned int s;
s=a+b;
return (s);
}
/ 函数功能:主函数
/ void main(void)
{
unsigned z;
z=sum(2008,2009);
P1=z/256; //取得z的高8位
P0=z%256; //取得z的低8位
while(1)
;
}

*实例28:用有参函数控制P0口8位LED流水速度 

#include

/ 函数功能:延时一段时间
/ void delay(unsigned char x)
{
unsigned char m,n;
for(m=0;m<x;m++)< span>
for(n=0;n<200;n++)
;
}
/
函数功能:主函数
/
void main(void)
{
unsigned char i;
unsigned char code Tab[ ]={0xFE,0xFD,0xFB,0xF7,0xEF,0xDF,0xBF,0x7F}; //流水灯控制码
while(1)
{
//快速流水点亮LED
for(i=0;i<8;i++) //共8个流水灯控制码
{
P0=Tab[i];
delay(100); //延时约60ms, (3 100 200=60 000μs) }
//慢速流水点亮LED
for(i=0;i<8;i++) //共8个流水灯控制码
{
P0=Tab[i];
delay(250); //延时约150ms, (3 250 200=150 000μs) }
}
}

来源:51单片机学习网

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 14

单片机执行指令

我们来思考一个问题,当我们在编程器中把一条指令写进单片机内部,然后取下单片机,单片机就可以执行这条指令。那么这条指令一定保存在单片机的某个地方,并且这个地方在单片机掉电后依然可以保持这条指令不会丢失,这是个什么地方呢?

这个地方就是单片机内部的只读存储器即ROM(READ ONLY MEMORY)。为什么称它为只读存储器呢?刚才我们不是明明把两个数字写进去了吗?原来在89C51中的ROM是一种电可擦除的ROM,称为FLASH ROM,刚才我们是用的编程器,在特殊的条件下由外部设备对ROM进行写的操作,在单片机正常工作条件下,只能从那面读,不能把数据写进去,所以我们还是把它称为ROM。

单片机数的本质和物理现象

我们知道,计算机可以进行数学运算,这令我们非常难以理解,它们只是一些电子元器件,怎么可以进行数学运算呢?

我们人类做数学题如37+45是这样做的,先在纸上写37,然后在下面写45,然后大脑运算最后写出结果,运算的原材料是37和45,结果是82都是写在纸上的,计算机中又是放在什么地方呢?

为了解决这个问题,先让我们做一个实验:这里有一盏灯,我们知道灯要么亮,要么不亮,就有两种状态,我们可以用‘0’和‘1’来代替这两种状态:规定亮为‘1’、不亮为‘0’。

现在放上三盏灯,一共有几种状态呢?我们列表来看一下:000 / 001 / 010 / 011 / 100 / 101 / 110 / 111。我们来看,这个000 / 001 / 101 不就是我们学过的的二进制数吗?本来,灯的亮和灭只是一种物理现象,可当我们把它们按一定的顺序排好后,灯的亮和灭就代表了数字了。

让我们再抽象一步,灯为什么会亮呢?是因为输出电路输出高电平,给灯通了电。因此,灯亮和灭就可以用电路的输出是高电平还是低电平来替代了。这样,数字就和电平的高、低联系上了。

单片机数位的含义

通过上面的实验我们已经知道:一盏灯亮或者说一根线的电平的高低,可以代表两种状态:0和1,实际上这就是一个二进制位。

因此我们就把一根线称之为一“位”,用BIT表示。

单片机字节的含义

一根线可以表示0和1,两根线可以表达00 / 01 / 10 / 11四种状态,也就是可以表达0~3,而三根可以表达0~7,计算机中通常用8根线放在一起,同时计数,就可以表示0~255一共256种状态。

这8根线或者8位就称之为一个字节(BYTE)。

单片机存储器的构造

存储器就是用来存放数据的地方。它是利用电平的高低来存放数据的,也就是说,它存放的实际上是电平的高、低,而不是我们所习惯认为的1234这样的数字,这样,我们的一个谜团就解开了。

一个存储器就象一个个的小抽屉,一个小抽屉里有八个小格子,每个小格子就是用来存放“电荷”的,电荷通过与它相连的电线传进来或释放掉。至于电荷在小格子里是怎样存的,就不用我们操心了,你可以把电线想象成水管,小格子里的电荷就象是水,那就好理解了。存储器中的每个小抽屉就是一个放数据的地方,我们称之为一个“单元”。

有了这么一个构造,我们就可以开始存放数据了,想要放进一个数据12,也就是00001100,我们只要把第二号和第三号小格子里存满电荷,而其它小格子里的电荷给放掉就行了。

可是问题出来了,一个存储器有好多单元,线是并联的,在放入电荷的时候,会将电荷放入所有的单元中,而释放电荷的时候,会把每个单元中的电荷都放掉。这样的话,不管存储器有多少个单元,都只能放同一个数,这当然不是我们所希望的。因此,要在结构上稍作变化。

需要在每个单元上有个控制线,想要把数据放进哪个单元,就把一个信号给这个单元的控制线,这个控制线就把开关打开,这样电荷就可以自由流动了。而其它单元控制线上没有信号,所以开关不打开,不会受到影响。

这样,只要控制不同单元的控制线,就可以向各单元写入不同的数据了。同样,如果要从某个单元中取数据,也只要打开相应的控制开关就行了。

单片机存储器的译码

那么,我们怎样来控制各个单元的控制线呢?这个还不简单,把每个单元的控制线都引到集成电路的外面不就行了吗?

事情可没那么简单,一片27512存储器中有65536个单元,把每根线都引出来,这个集成电路就得有6万多个脚?不行,怎么办?要想法减少线的数量。

有一种方法称这为译码,简单介绍一下:一根线可以代表2种状态,2根线可以代表4种状态,3根线可以代表8种,256种状态又需要几根线代表?8根线,所以65536种状态我们只需要16根线就可以代表了。

单片机存储器的选片概念

至此,译码的问题解决了,让我们再来关注另外一个问题。送入每个单元的八根线是用从什么地方来的呢?它就是从计算机上接过来的,一般地,这八根线除了接一个存储器之外,还要接其它的器件。

这样问题就出来了,这八根线既然不是存储器和计算机之间专用的,如果总是将某个单元接在这八根线上,就有问题出现了:比如这个存储器单元中的数值是0FFH另一个存储器的单元是00H,那么这根线到底是处于高电平,还是低电平?怎样分辩?

办法很简单,当外面的线接到集成电路的引脚进来后,不直接接到各单元去,中间再加一组开关就行了。平时我们让开关打开着,如果确实是要向这个存储器中写入数据,或要从存储器中读出数据,再让开关接通就行了。

这组开关由三根引线选择:读控制端、写控制端和片选端。要将数据写入片中,先选中该片,然后发出写信号,开关就合上了,并将传过来的数据(电荷)写入片中。如果要读,先选中该片,然后发出读信号,开关合上,数据就被送出去了。

读和写信号同时还接入到另一个存储器,但是由于片选端不同,所以虽有读或写信号,但没有片选信号,所以另一个存储器不会“误会”而开门,造成冲突。那么会不同时选中两片芯片呢?

只要是设计好的系统就不会,因为它是由计算控制的,而不是我们人来控制的,如果真的出现同时出现选中两片的情况,那就是电路出了故障了,这不在我们的讨论之列。

单片机的总线概念

从上面的介绍中我们已经看到,用来传递数据的八根线并不是专用的,而是很多器件大家共用的。

所以我们称之为数据总线,总线英文名为BUS,总即公交车道,谁也可以走。而十六根地址线也是连在一起的,称之为地址总线。

来源:STM32嵌入式开发

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 19

最近有网友大概问了这样的问题:单片机项目偶尔经常出现异常,不知道是程序跑飞了,还是进入某个死循环了。

因为发生概率比较低,也没有规律,所以没办法在线调试查找问题。

结合这个问题,给大家分享一下用ST-LINK Utility识别单片机程序是否跑飞。

利用ST-LINK Utility查看内核运行状态

做想要知道单片机是否跑飞,可以利用ST-LINK Utility查看内核运行状态。

利用STM32 ST-LINK Utility工具中Hot Plug热插拔模式,查看内核运行状态。

1.STM32 ST-LINK Utility模式说明

即设置中三种模式选择:

1.png

主要有三种,在这个工具的用户手册中可以看到:

2.jpg

大概意思是:

Normal:常规模式

连接目标(芯片)之后,芯片复位,然后暂停(halted)。

Connect Under Reset:连接复位模式

主要用于JTAG/SWD引脚被禁用的时候。

Hot Plug:热插拔模式

连接目标芯片,不复位/重置芯片,则此时可以查看芯片状态。

2.查看内核运行状态

硬件连接好:Target -> MCU Core

3.jpg

当你的程序跑飞,内核就处于异常状态,这里可以查看MCU内核的运行状态,简单的说就可以查看PC跑到哪儿去了(你应用程序跑如果超过相应区域,说明跑飞了)。

4.png

举个例:你应用程序存储在0x08010000 --- 0x08020000 这个范围内,正常运行应用程序应该在这个范围内,但是你发现PC的值为0x08025000(不在范围内),那就说明跑飞了。

这里可以执行的操作有:运行、暂停、系统复位、内核复位、单步运行、读内核寄存器。类似于IDE中的在线调试(IDE在线调试需要有源代码,而这里不需要源代码,可直接查看PC执行到的地址)。

提示:PC:Program Counter即程序计数器寄存器,指向当前执行程序的地址,如果修改它的值,就能改变程序的执行流。(具体可以参看Cortex-M3内核寄存器所在章节)

Option Bytes选项字配置

这里额外说一下选项字配置的内容。

对Option Bytes操作常用的一个就是对Flash加密(读/写保护)。当然,读写保护可以通过程序代码实现,也可以通过这里说的选项字设置来实现。

拿F401举例:Target ->Option Bytes

5.png
STM32 ST-LINK Utility工具还有些特殊功能,可能我们不常用,比如:command line interface命令行接口、external loader developing加载外部程序等,感兴趣的朋友可以参看用户手册研究一下。
好了,本文就分享以上几点内容,希望对你们有所帮助。

来源:strongerHuang

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 30

其实,单片机就是个小计算机。大计算机少不了的数据存储系统,单片机一样有,而且往往和CPU集成在一起,显得更加小巧灵活。

直到90年代初,国内容易得到的单片机是8031:不带存储器的芯片,要想工作,必须外加RAM和ROM,于是单片机成了3片机......

而现在不同了,无论大的,还是小的,又是51,又是AVR,又是STC,还有什么430、PIC……都各说各的好,可谁也不敢说“我不要存储器”。

单片机的数据存储手段

程序存储器里面存放的是单片机的灵魂:工作程序。

小的可能只有1KB,最多只能装1024条8位数据,因为实际指令还有许多2字节,3字节指令,所以它还装不下1024条指令。大的也有128KB的。这些8位数据,要么在工厂里做模子光刻进去,要么一次性的烧写进去。

业余或开发,最多也就是用编程器这么一个特殊工具,把调试成功的机器码装载进去,或者像AVR单片机那样自己花几块钱做一条下载线,把电脑里这些东西灌进去(或许是AVR最吸引人之处)。

它一旦进驻电脑的程序存储器中,除了借助上述装置便不能自由改写,在单片机运行时,只是从其中读出指令或固定的数据,所以给程序存储器一个“只读存储器”的别名,简写为ROM,包括用编程器写紫外线擦除内容的EPROM、用电擦除的EEPROM和现在新兴的FLASH ROM。相关文章:EEPROM和Flash这样讲,我早就懂了。

一次性写入的ROM,仅用于电路和程序固定的批量产品中,实际工作起来,都是一样的。

为了定位ROM中的数据,每个8位存储单元都有一个固定的“地址”,通常用16进制数表示。例如,对于一个所谓4K的ROM,地址从0000H到0FFFH(即从0000,0001...4095),单片机运行时从哪个地址取数据,完全由程序本身决定,并不要我们干预。

记住,给单片机一通电,它经过一个短暂的复位过程,立即转向ROM的最低地址0000H,在这里面放置的往往是一条“跳转”指令,它从这里一步跳到另一个地址:程序的真正起始地址,例如51机的0080H。

ROM是程序存储器,除了指令外,还包括运行程序必须的某些固定数据,例如:数据表。假如,我们要求在单片机的接口上输出00H到FFH(255)按正弦半波变化的数值,每秒10000次。如果硬要它按照公式一个个计算,对于它来说未免力不从心。可是我们可以把预先计算好的数值存入ROM中,到时候直接取出不是好多了?

又如一个重要的应用:大家一定见过不少单片机的东西上面都有数码显示,那些个数字其实就是用单片机的口线控制数码管的字段电极电位。这些字形也是存放在ROM中的字模表,各个字模和0-9的数字(机器内当然是0000-0101二进制数)对应起来。常见的共阳极7段数码管,必须在阳极加正电,7个阴极都是地电位,才能显示数字"8",数字8对应的显示字码值是二进制数“10000000“(那个1对应的是小数点,高电位不让它显示)。

2. 数据存储器RAM

这是个可以随时存取数据的一块存储器,也就是可以读(取)也可以写(存)的存储器,简称RAM。

现在的单片机里面使用的RAM,属于静态RAM或SRAM,这个和电脑用的内存条有所不同。只要你把数据写入SRAM后,不断电或者不清除掉,这个数据就一直保存在那里。电脑用的是动态RAM,要不断给它加刷新脉冲才能保存数据。

因为单片机处理的信息量比电脑小很多,所以它带的RAM也比较少:从完全不带、带128、256、...1K、2K,到4K,比ROM少多了。

因为实际上RAM只是作为数据临时存放的地方,除非进行图像处理需要存放大量的数据外。一般对于执行较简单任务的单片机,有这么多也够用,如果实在不够用也只能采取外加SRAM如6116、6264等等来扩展。

1.jpg

为了对RAM单元存取8位二进制数,当然也得和ROM一样用“地址”来标示它的具体位置。假如某单片机有1K(1024)RAM,它的地址也是从0000到1024,或16进制数的0000H到03FFH。可见,和ROM的地址是一样的。

3. 会不会混淆不清?

答案是不会的,因为读ROM是由单片机的程序指针或转移指令或查表指令进行,而这些指令是不会进入RAM区的;读写RAM是另外的数据传送指令,也不会进入ROM区。

这点也是和电脑不同之处,后者程序和数据都在内存条里面,地址不同,如果窜位了就会造成不可预见后果。单片机的这种存储器结构也称为哈佛结构。

RAM在单片机里的用途

RAM在单片机里的用途,主要是存放临时数据。

例如用单片机测温,每秒测1次,显示1分钟的平均值(1分钟更新一次):

我们先通过传感器、放大电路、A/D转换,把温度这个模拟量转变为成比例的二进制数,然后每秒钟1次把数字量通过输入口顺序存入到单片机的RAM中,然后对他们进行两两求和再平均的计算,最后的数值显示出来,然后把这60个存储单元统统写0清除旧数据,下次又是如此循环进行。

总结

另外,在单片机里面还有若干寄存器,数量不多但是作用很大,除了暂存数据,还可以交换、加工、传递等等,以及随时记录单片机当前处于什么状态,输入输出口也是作为特殊功能的寄存器存在,具体各有不同,就不是随便说说可以搞清楚的,要看有关书籍了。

来源:ARM与嵌入式

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 12

智能科技的迅速发展使得我们的日常生活变得更加便捷和舒适。智能马桶作为其中一种智能家居产品,通过单片机接受和处理来自传感器的数据,然后通过控制模块对智能马桶的各项功能进行控制,实现对智能马桶的全面控制和调节。本文将介绍CW32单片机在智能马桶的详细应用。

1.png

图:CW32的智能马桶控制板

CW32单片机在智能马桶的应用介绍

1.温度感应与控制

智能马桶内设有温度传感器,通过CW32单片机的处理,可以实时感知到用户所需的座圈温度并根据用户的设定进行调节。当温度过高或过低时,单片机将发出信号,通过控制模块对座圈的加热或制冷进行调节,提供更加舒适的使用体验。

2.png

图:CW32在智能马桶控制板的应用框图

2.智能冲洗功能控制

CW32单片机通过接收用户的操作指令,进行流量检测,根据用户所需的水流大小和冲洗时间,精确地控制水泵的工作和停止。

3.智能电量监控与节能功能

智能马桶内置电池或电源模块,CW32单片机可以实时监测电池的电量,并根据电量情况进行智能的调节。当电量不足时,单片机会通过控制模块对电源进行调节,以延长电池的使用寿命。例如,降低加热功率或减少冲洗时间等,通过智能的电量管理,实现了对电能的高效利用和节约。

这款智能马桶采用的是武汉芯源半导体32位低功耗CW32L083系列,该系列产品集成了主频高达64MHz的ARM® Cortex®-M0+ 内核、最多256KB FLASH 、最多 24KBRAM、最多87路GPIO,以及一系列增强型外设。

CW32L083系列产品优势介绍

1、系统特性

● ARM® Cortex®-M0+ 内核;

● 最高主频64MHz;

● 温度范围:-40℃ 至 85℃;

● 宽压供电:1.65V 至 5.5V;

2、高可靠性

● HBM ESD 8KV;MM ESD、CDM ESD、Latch up@105℃全面达到JEDEC较高等级;

● EFT 4KV,IEC61000-4-4 Class:4(Power)/4(IO);

● 支持奇偶校验;

● 符合IEC60730,IEC61508 功能安全设计规范;

3、超低功耗

● 支持0.6uA@3.3V深度休眠模式;支持1.7uA@3.3V深度休眠模式+IWDT工作;

● 深度休眠模式下电流0.6uA,超低功耗唤醒时间仅需4us;

● 支持灵活的中断唤醒及事件唤醒;

4、内部资源

● 最多87路GPIO;

● 1路12位ADC;

● 六路低功耗 UART,支持小数波特率;

● 两路 SPI 接口 12Mbit/s;

● 两路 I2C 接口 1Mbit/s;

● 16 位高级控制定时器;四组 16 位通用定时器;三组 16 位基本定时器;一组 16 位超低功耗定时器;窗口看门狗定时器;独立看门狗定时器;

● 1路低功耗定时器(LPTIM)

● 4×56、6×54 或 8×52 LCD 段码液晶驱动器

● TRNG 真随机数发生器

● AES 高级加密标准模块

CW32L083系列产品适用于对FLASH、RAM、GPIO等资源需求较大,且有LCD显示、低功耗要求的应用场合,该系列目前可提供LQFP64、LQFP80、LQFP100三种封装形式。

3.png

图:CW32L083系列封装展示

CW32L083系列产品非常适合各种小、中型电子产品的应用领域,比如医疗和手持设备、PC外围设备、游戏设备、运动装备、报警系统、智能门锁、有线和无线传感器模块、表计等产品。

来源: 武汉芯源半导体

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 19

虽然现在通信方式有很多类型,但串口依然是嵌入式领域应用最广泛的通信方式之一。

本文给介绍如何使用带FIFO的串口来减少接收中断次数,通过一种自定义通讯协议格式,给出帧打包方法;之后介绍一种特殊的串口数据发送方法,可在避免使用串口发送中断的情况下,提高系统的响应速度。

1.简介

串口由于使用简单,价格低廉,配合RS485芯片可以实现长距离、抗干扰能力强的局域网络而被广泛使用。随着产品功能的增多,需要处理的任务也越来越复杂,系统任务也越来越需要及时响应。绝大多数的现代单片机(ARM7、Cortex-M3)串口都带有一定数量的硬件FIFO,本文将介绍如何使用硬件FIFO来减少接收中断次数,提高发送效率。在此之前,先来列举一下传统串口数据收发的不足之处:每接收一个字节数据,产生一次接收中断。不能有效的利用串口硬件FIFO,减少中断次数。应答数据采用等待发送的方法。由于串行数据传输的时间远远跟不上CPU的处理时间,等待串口发送完当前字节再发送下一字节会造成CPU资源浪费,不利于系统整体响应(在1200bps下,发送一字节大约需要10ms,如果一次发送几十个字节数据,CPU会长时间处于等待状态)。应答数据采用中断发送。增加一个中断源,增加系统的中断次数,这会影响系统整体稳定性(从可靠性角度考虑,中断事件应越少越好)。针对上述的不足之处,将结合一个常用自定义通讯协议,提供一个完整的解决方案。

2.串口FIFO

串口FIFO可以理解为串口专用的缓存,该缓存采用先进先出方式。数据接收FIFO和数据发送FIFO通常是独立的两个硬件。串口接收的数据,先放入接收FIFO中,当FIFO中的数据达到触发值(通常触发值为1、2、4、8、14字节)或者FIFO中的数据虽然没有达到设定值但是一段时间(通常为3.5个字符传输时间)没有再接收到数据,则通知CPU产生接收中断;发送的数据要先写入发送FIFO,只要发送FIFO未空,硬件会自动发送FIFO中的数据。写入发送FIFO的字节个数受FIFO最大深度影响,通常一次写入最多允许16字节。上述列举的数据跟具体的硬件有关,CPU类型不同,特性也不尽相同,使用前应参考相应的数据手册。

3.数据接收与打包

FIFO可以缓存串口接收到的数据,因此我们可以利用FIFO来减少中断次数。以NXP的lpc1778芯片为例,接收FIFO的触发级别可以设置为1、2、4、8、14字节,推荐使用8字节或者14字节,这也是PC串口接收FIFO的默认值。这样,当接收到大量数据时,每8个字节或者14个字节才会产生一次中断(最后一次接收除外),相比接收一个字节即产生一个中断,这种方法串口接收中断次数大大减少。

将接收FIFO设置为8或者14字节也十分简单,还是以lpc1778为例,只需要设置UART FIFO控制寄存器UnFCR即可。

接收的数据要符合通讯协议规定,数据与协议是密不可分的。通常我们需要将接收到的数据根据协议打包成一帧,然后交由上层处理。下面介绍一个自定义的协议帧格式,并给出一个通用打包成帧的方法。

自定义协议格式如图3-1所示。

1.png

  • 帧首:通常是3~5个0xFF或者0xEE

  • 地址号:要进行通讯的设备的地址编号,1字节

  • 命令号:对应不同的功能,1字节

  • 长度:数据区域的字节个数,1字节

  • 数据:与具体的命令号有关,数据区长度可以为0,整个帧的长度不应超过256字节

  • 校验:异或和校验(1字节)或者CRC16校验(2字节),本例使用CRC16校验

下面介绍如何将接收到的数据按照图3-1所示的格式打包成一帧。

3.1 定义数据结构

typedef struct {  
      uint8_t * dst_buf;                  //指向接收缓存  
      uint8_t sfd;                        //帧首标志,为0xFF或者0xEE  
      uint8_t sfd_flag;                   //找到帧首,一般是3~5个FF或EE  
      uint8_t sfd_count;                  //帧首的个数,一般3~5个  
      uint8_t received_len;               //已经接收的字节数  
      uint8_t find_fram_flag;             //找到完整帧后,置1  
      uint8_t frame_len;                  //本帧数据总长度,这个区域是可选的  
}find_frame_struct;

3.2 初始化数据结构,一般放在串口初始化中

/** 
* @brief    初始化寻找帧的数据结构 
* @param    p_fine_frame:指向打包帧数据结构体变量 
* @param    dst_buf:指向帧缓冲区 
* @param    sfd:帧首标志,一般为0xFF或者0xEE 
*/  
void init_find_frame_struct(find_frame_struct * p_find_frame,uint8_t *dst_buf,uint8_t sfd)  
{  
     p_find_frame->dst_buf=dst_buf;  
     p_find_frame->sfd=sfd;  
     p_find_frame->find_fram_flag=0;  
     p_find_frame->frame_len=10;       
     p_find_frame->received_len=0;  
     p_find_frame->sfd_count=0;  
     p_find_frame->sfd_flag=0;  
}

3.3 数据打包程序

/** 
* @brief    寻找一帧数据  返回处理的数据个数 
* @param    p_find_frame:指向打包帧数据结构体变量 
* @param    src_buf:指向串口接收的原始数据 
* @param    data_len:src_buf本次串口接收到的原始数据个数 
* @param    sum_len:帧缓存的最大长度 
* @return   本次处理的数据个数 
*/  
uint32_t find_one_frame(find_frame_struct * p_find_frame,const uint8_t * src_buf,uint32_t data_len,uint32_t sum_len)  
{  
     uint32_t src_len=0;  
       
     while(data_len--)  
     {  
         if(p_find_frame ->sfd_flag==0)                        
         {   //没有找到起始帧首  
             if(src_buf[src_len++]==p_find_frame ->sfd)  
             {  
                 p_find_frame ->dst_buf[p_find_frame ->received_len++]=p_find_frame ->sfd;  
                 if(++p_find_frame ->sfd_count==5)          
                 {  
                     p_find_frame ->sfd_flag=1;  
                     p_find_frame ->sfd_count=0;  
                     p_find_frame ->frame_len=10;  
                 }  
             }  
             else  
             {  
                 p_find_frame ->sfd_count=0;   
                 p_find_frame ->received_len=0;   
             }  
         }  
         else   
         {   //是否是"长度"字节? Y->获取这帧的数据长度  
             if(7==p_find_frame ->received_len)                
             {  
            p_find_frame->frame_len=src_buf[src_len]+5+1+1+1+2; //帧首+地址号+命令号+数据长度+校验  
                   
                 if(p_find_frame->frame_len>=sum_len)  
                 {   //这里处理方法根据具体应用不一定相同  
                     MY_DEBUGF(SLAVE_DEBUG,("数据长度超出缓存!\n"));  
                     p_find_frame->frame_len= sum_len;       
                 }  
             }  
               
             p_find_frame ->dst_buf[p_find_frame->received_len++]=src_buf[src_len++];  
               
             if(p_find_frame ->received_len==p_find_frame ->frame_len)                  
             {  
                 p_find_frame ->received_len=0;              //一帧完成    
                 p_find_frame ->sfd_flag=0;  
                 p_find_frame ->find_fram_flag=1;   
                      
                 return src_len;  
             }  
         }  
     }  
     p_find_frame ->find_fram_flag=0;  
     return src_len;  
}

使用例子:

定义数据结构体变量:

find_frame_structslave_find_frame_srt;

定义接收数据缓冲区:

#define SLAVE_REC_DATA_LEN  128
uint8_t slave_rec_buf[SLAVE_REC_DATA_LEN];

在串口初始化中调用结构体变量初始化函数:

init_find_frame_struct(&slave_find_frame_srt,slave_rec_buf,0xEE);

在串口接收中断中调用数据打包函数:

find_one_frame(&slave_find_frame_srt,tmp_rec_buf,data_len,SLAVE_REC_DATA_LEN);

其中,rec_buf是串口接收临时缓冲区,data_len是本次接收的数据长度。

4.数据发送

前文提到,传统的等待发送方式会浪费CPU资源,而中断发送方式虽然不会造成CPU资源浪费,但又增加了一个中断源。在我们的使用中发现,定时器中断是几乎每个应用都会使用的,我们可以利用定时器中断以及硬件FIFO来进行数据发送,通过合理设计后,这样的发送方法即不会造成CPU资源浪费,也不会多增加中断源和中断事件。

需要提前说明的是,这个方法并不是对所有应用都合适,对于那些没有开定时器中断的应用本方法当然是不支持的,另外如果定时器中断间隔较长而通讯波特率又特别高的话,本方法也不太适用。公司目前使用的通讯波特率一般比较小(1200bps、2400bps),在这些波特率下,定时器间隔为10ms以下(含10ms)就能满足。如果定时器间隔为1ms以下(含1ms),是可以使用115200bps的。

本方法主要思想是:定时器中断触发后,判断是否有数据要发送,如果有数据要发送并且满足发送条件,则将数据放入发送FIFO中,对于lpc1778来说,一次最多可以放16字节数据。之后硬件会自动启动发送,无需CPU参与。

下面介绍如何使用定时器发送数据,硬件载体为RS485。因为发送需要操作串口寄存器以及RS485方向控制引脚,需跟硬件密切相关,以下代码使用的硬件为lpc1778,但思想是通用的。

4.1 定义数据结构

/*串口帧发送结构体*/  
typedef struct {  
      uint16_t send_sum_len;          //要发送的帧数据长度  
      uint8_t  send_cur_len;          //当前已经发送的数据长度  
      uint8_t  send_flag;             //是否发送标志  
      uint8_t * send_data;            //指向要发送的数据缓冲区  
}uart_send_struct;

4.2 定时处理函数

/** 
* @brief    定时发送函数,在定时器中断中调用,不使用发送中断的情况下减少发送等待 
* @param    UARTx:指向硬件串口寄存器基地址 
* @param    p:指向串口帧发送结构体变量 
*/  
#define FARME_SEND_FALG 0x5A          
#define SEND_DATA_NUM   12  
static void uart_send_com(LPC_UART_TypeDef *UARTx,uart_send_struct *p)  
{  
     uint32_t i;  
     uint32_t tmp32;  
       
     if(UARTx->LSR &(0x01<<6))                      //发送为空  
     {         
         if(p->send_flag==FARME_SEND_FALG)  
         {                          
             RS485ClrDE;                             // 置485为发送状态  
               
             tmp32=p->send_sum_len-p->send_cur_len;  
             if(tmp32>SEND_DATA_NUM)                 //向发送FIFO填充字节数据  
             {  
                 for(i=0;i<SEND_DATA_NUM;i++)  
                 {  
                     UARTx->THR=p->send_data[p->send_cur_len++];  
                 }  
             }  
             else  
             {  
                 for(i=0;i<tmp32;i++)  
                 {  
                     UARTx->THR=p->send_data[p->send_cur_len++];  
                 }  
                 p->send_flag=0;                      
             }  
         }  
         else  
         {  
             RS485SetDE;  
         }  
     }  
}

其中,RS485ClrDE为宏定义,设置RS485为发送模式;RS485SetDE也为宏定义,设置RS485为接收模式。

使用例子:

定义数据结构体变量:

uart_send_struct uart0_send_str;

定义发送缓冲区:

uint8_t uart0_send_buf[UART0_SEND_LEN];

根据使用的硬件串口,对定时处理函数做二次封装:

void uart0_send_data(void)
{
   uart_send_com(LPC_UART0,&uart0_send_str);
}

将封装函数uart0_send_data();放入定时器中断处理函数中;

在需要发送数据的地方,设置串口帧发送结构体变量:

uart0_send_str.send_sum_len=data_len;       //data_len为要发送的数据长度
uart0_send_str.send_cur_len=0;              //固定为0
uart0_send_str.send_data=uart0_send_buf;    //绑定发送缓冲区
uart0_send_str.send_flag=FARME_SEND_FALG;   //设置发送标志

5.总结

本文主要讨论了一种高效的串口数据收发方法,并给出了具体的代码实现。在当前处理器任务不断增加的情况下,提供了一个占用资源少,可提高系统整体性能的新的思路。

来源:嵌入式专栏

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 23

讲述EMC的定义,EMC在单片机应用系统的测试方法,EMC新器件新材料的应用以及故障排除技术。只要从事电子产品的研发、生产或者供应,就必须进行EMC电磁兼容的检测工作。

引言

所谓EMC就是:设备或系统在其电磁环境中能正常工作,且不对该环境中任何事物构成不能承受的电磁骚扰的能力。EMC测试包括两大方面内容:对其向外界发送的电磁骚扰强度进行测试,以便确认是否符合有关标准规定的限制值要求;对其在规定电磁骚扰强度的电磁环境条件下进行敏感度测试,以便确认是否符合有关标准规定的抗扰度要求。

对于从事单片机应用系统设计的工程技术人员来说,掌握一定的EMC测试技术是十分必要的。EMC是电磁兼容(Electro-MagneTIc CompaTIbility)的缩写,它包括电磁干扰(EMI)和电磁敏感性(EMS)两部分。由于电器产品在使用时对其它电器有电磁干扰,或受到其它电器的电磁干扰,它不仅关系到产品工作的可靠性和安全性,还可能影响其它电器的正常工作,甚至导致安全危险。

单片机系统EMC测试

(1)测试环境


为了保证测试结果的准确和可靠性,电磁兼容性测量对测试环境有较高的要求,测量场地有室外开阔场地、屏蔽室或电波暗室等。


(2)测试设备


电磁兼容测量设备分为两类:一类是电磁干扰测量设备,设备接上适当的传感器,就可以进行电磁干扰的测量;另一类是在电磁敏感度测量,设备模拟不同干扰源,通过适当的耦合/去耦网络、传感器或天线,施加于各类被测设备,用作敏感度或干扰度测量。

(3)测量方法


电磁兼容性测试依据标准的不同,有许多种测量方法,但归纳起来可分为4类;传导发射测试、辐射发射测试、传导敏感度(抗扰度)测试和辐射敏感度(抗扰度)测试。


(4)测试诊断步骤


图1给出了一个设备或系统的电磁干扰发射与故障分析步骤。按照这个步骤进行,可以提高测试诊断的效率。


(5)测试准备

①试验场地条件:EMC测试实验室为电波半暗室和屏蔽室。前者用于辐射发射和辐射敏感测试,后者用于传导发射和传导敏感度测试。

②环境电平要求:传导和辐射的电磁环境电平最好远低于标准规定的极限值,一般使环境电平至少低于极限值6dB。

③试验桌。

④测量设备和被测设备的隔离。

⑤敏感性判别准则:一般由被测方提供,并实话监视和判别,以测量和观察的方式确定性能降低的程度。

⑥被测设备的放置:为保证实验的重复性,对被测设备的放置方式通常有具体的规定。


1.jpg

(6)测试种类

传导发射测试、辐射发送测试、传导抗扰度测试、辐射抗扰度测试。

(7)常用测量仪

电磁干扰(EMI)和电磁敏感度(EMS)测试,需要用到许多电子仪器,如频谱分析仪、电磁场干扰测量仪、信号源、功能放大器、示波器等。由于EMC测试频率很宽(20Hz~40GHz)、幅度很大(μV级至kW级)、模式很多(FM、AM等)、姿态很多(平放、斜放等),因此正确地使用电子仪器非常重要。测量电磁干扰的合适仪器是频谱分析仪。频谱分析仪是一种将电压幅度随频率变化的规律显示出来的仪器,它显示的波形称为频谱。频谱分析仪克服了示波器在测量电磁干扰中的缺点,能够精确测量各个频率上的干扰强度,用频谱分析仪可以直接显示出信号的各个频谱分量。

在解决电磁干扰问题时,最重要的一个问题是判断干扰的来源。只有准确将干扰源定位后,才能够提出解决干扰的措施。根据信号的频率来确定干扰源泉是最简单的方法,因为在信号的所有特征中,频率特征是最稳定的,并且电路设计人员往往对电路中各个部位的信号频率都十分清楚。因此,只要知道了干扰信号的频率,就能够推测出干扰是哪个部位产生的。对于电磁干扰信号,由于其幅度往往远小于正常工作信号,用频谱分析仪做这种测量是十分简单的。由于频谱分析仪的中频带宽较窄,因此能够将与干扰信号频率不同的信号滤除掉,精确地测量出干扰信号频率,从而判断产生干扰信号的电路。

电磁兼容故障排除技术

(1)传导型问题的解决

①通过串联一个高阻抗来减少EMI电流。

②通过并联一个低阻抗将EMI电流短路到地或引到其它回路导体。

③通过电流隔离装置切断EMI电流。

④通过其自身作用来抑制EMI电流。

(2)电磁兼容的容性解决方案


一种常见的现象是不把滤波电容的一侧看成直接与一个分离的阻抗相连,而看成与传输线相连。典型的情况是,当一条输入输出线的长度达到或超过1/4波长时,该传输线变“长”。

实际可以用下式近似表示这种变化:

l≥55/f

式中:l单元为m,f单位为MHz。这个公式考虑了平均传播速度,它是自由空间理论的0.75倍。

a. 电介质材料及容差

电磁干扰滤波使用的大部分电容是无极性电容。

b. 差模(线到线)滤波电容性电容。

c. 共模(线到地/机壳)滤波电容

共模(CM)去耦通常使用小电容(10~100nF)。小电容可以将不期望的高频电流在其进入敏感电路之前或在其离噪声电路较远时就将其短路到机壳上去。为了得到良好的高频衰减电路,减小或消除寄生电感是关键之所在。因此有必要使用超短导线,尤其希望使用无引线元器件。


(3)感性、串联损耗电磁兼容解决方案


就电容而言,Zs和Z1如果不是纯电阻的话,在计算频率时,要使用它们的实际值。电容器串联在电源或信号电路时,必须满足:

①流过的工作电流不应该引起电感过热或过大的有过之而无不及降;

②流过的电流不能引起电感磁饱和,尤其是对高导磁材料是毫无疑问的。
解决方案有以下几种:


  • 磁芯材料;
  • 铁氧体和加载铁氧体的电缆;
  • 电感、差模和共模;
  • 接地扼流圈;
  • 组合式电感电容元件。

(4)辐射型问题的解决

在很多情况下,辐射电磁干扰问题可能在传导阶段产生并被排除,还有些解决方案是可以抑制干扰装置在辐射传输通道上,就像场屏蔽那样工作。根据屏蔽理论,这种屏蔽的效果主要取决于电磁干扰源的频率、与屏蔽装置之间的距离以及电磁干扰场的特性——电场、磁场或者平面波。


①导体带。

使用铜或铝带要吧简单快速地建立一种直接的屏蔽和低阻连连接或总线。它们对于临时的解决方案和相对永久的解决方案来说是很方便的。厚度在0.035~0.1mm之间,并且背面带有导电黏合剂以便安装。如果使用铜导电带,其通过电阻约20mΩ/cm2。应用场合:电气屏蔽罩;发生故障时泄露点定位;作为一个应急的解决方案,将塑料连接器变成金属的、屏蔽普通的扁平电缆等。

②网状屏蔽带和拉链式外套。

涂锡的钢网带:主要用来安装在一个已经装配好的电费护套上作为一种易安装的绷带型的屏蔽罩。为了降低电费的磁场辐射或敏感问题,钢网带是一种有效的解决方案。

拉链式屏蔽外套:当有明显迹象表明电费是主要的引起EMI耦合的原因时使用。

③EMI密封垫。

应用场合:当下述条件存在,并且需要真正的SE时,EMI密封垫是最常用的解决辐射问题、敏感问题、ESD、电磁脉冲和TEMPEST问题的方法。

*已经把机箱泄漏确认为主要的辐射路径。

*啮合面不够光滑、平整或不够硬、本身无法提供良好的连接接触。

④窗口和通风板的EMI屏蔽:

适合对孔径的屏蔽。

平面波的大概模型是:
SE≈104(-20-lgl)-20lgf

式中,SE单位为dB;l为网格或网孔的尺寸,单位为mm;f单位为MHz。当然,随着频率的下降,网孔的屏蔽效率SE的上限受限于金属本身。在近区场,对H场的屏蔽,其屏蔽功率SHE不受频率的影响,可由下式近似得出:

SEH≈10lg(πr/l)

其中,r为源到屏蔽罩之间的距离,l为网孔尺寸,两者单位均为mm。


⑤导电涂料:

应用于在系统的塑料外壳建立EMI屏蔽罩、发送现有普通的或恶化的导电表面的屏蔽效能SE、防止ESD或静电积累现象、增大结合面或密封垫片的接触面积。


⑥导电箔:

铝是一种良导体,在10MHz以下没有吸收损耗,但它对于电场的任何频率都有较好的反射损耗。应用场合请参阅有关资料。

⑦导电布:

可应用于任何100kHz到GHz级频率范围需要达到30~30dB衰减的立体屏蔽场合中

结语

在实际EMC测试应用中,除了通过标准资格实验室的鉴定测试以外,还有两种可行的方法也是被业界所认可的:TCF(Technical ConstrucTIon File)和Self CeriTIfication(自检证明)。抗干扰能力测试是十分实用的测试项目。实现电磁兼容的最好办法是,将所有的数字及模拟电路均视为对高频信号响应的电路,用高频设计方法来处理电费屏蔽、PCB布线和共模滤波。采用整块地平面和电源面也很重要,对模拟电路也该如此,这样做有利于限制高频共模环环。大多数瞬态干扰均属高频,并产生很强的辐射能量。

来源:ST中文论坛

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 24

该系列单片机新增电压电平转换功能,有助于提高灵活性并降低系统成本

从手机、汽车到智能恒温器和家用电器,越来越多日常设备与云端相连。随着连接性增多,在芯片层面部署先进的安全措施以保护固件和数据,就变得至关重要。为了应对当前和不断扩大的安全威胁,Microchip Technology Inc.(微芯科技公司)今日发布PIC18-Q24 系列单片机(MCU

pREZ3aNBYN.jpg

为应对在嵌入式系统中对器件进行恶意重新编程的威胁,PIC18-Q24单片机引入了编程和调试接口禁用(PDID)功能。启用后,这一增强型代码保护功能将锁定对编程/调试接口的访问,并阻止未经授权的读取、修改或擦除固件的尝试。

Microchip 8位单片机业务部副总裁Greg Robinson表示:“系统的安全性取决于最薄弱的环节。任何可编程组件都可能存在漏洞,因此必须实施增强型保护功能,以防止潜在的黑客攻击。MicrochipPIC18-Q24系列单片机在设计最初就将高级安全性放在首位,以帮助客户在系统底层抵御威胁。”

由于许多安全系统经常与各种传感器、存储芯片和处理器连接和通信,因此PIC18-Q24系列单片机具有多电压 I/OMVIO)功能。该功能无需使用外部电平转换器,使单片机能以不同工作电压连接数字输入或输出。除了降低电路板复杂性和物料清单(BOM)成本外,MVIO功能还使PIC18-Q24系列单片机特别适合用作系统管理处理器,为大型处理器执行监控和遥测任务。嵌入式系统中这些看似常规的任务通常最容易受到潜在黑客的攻击。

PIC18-Q24系列单片机还提供不可更改的引导加载程序选项,适用于需要安全升级固件的应用。如需了解更多信息,请访问MicrochipPIC® MCU网页

开发工具

PIC18-Q24系列单片机由Microchip开发生态系统提供全面支持,可与MPLAB®代码配置器(MCC)集成设计。PIC18F56Q24 Curiosity Nano评估工具包(EV01E86A)为使用PIC18-Q24系列进行设计提供全面支持。这些功能可协同实现无缝嵌入式开发体验,缩短产品上市时间。

供货与定价

如需了解更多信息或购买,请联系Microchip销售代表、全球授权分销商或访问Microchip采购和客户服务网站www.microchipdirect.com

Microchip Technology Inc. 简介

Microchip Technology Inc.是致力于智能、互联和安全的嵌入式控制解决方案的领先供应商。其易于使用的开发工具和丰富的产品组合让客户能够创建最佳设计,从而在降低风险的同时减少系统总成本,缩短上市时间。Microchip的解决方案为工业、汽车、消费、航天和国防、通信以及计算市场中125千多家客户提供服务。Microchip总部位于美国亚利桑那州Chandler市,提供出色的技术支持、可靠的产品交付和卓越的质量。详情请访问公司网站www.microchip.com

围观 8

对于搞单片机的特别用8051系列工程师来说,谈到单片机的RTOS,很多时候会问一句:“为什么要用RTOS?单片机就这一点资源,使用RTOS能保证效率吗?”

对于这个问题,我会反问:“你用单片机的目的是什么?是为了用单片机的C编程,单片机的汇编编程甚至于用单片机的二进制指令编程?”上个世纪80年代,工程师用二进制指令给Z80编程,现在还有谁在用?现在还有人死抱着汇编不放,但越来越多的人工程师使用C编程(我起初也是使用汇编的),为什么?

因为我们的目的是在有限的时间甚至是不充足的时间内把项目保质保量的完成!使用什么工具和方法是次要的(如果你的项目以成本放在第一位,则另当别论,这时,也是要考虑开发时间的)。时间就是金钱啊,一个产品在单片机上增加些许成本是可以接受的。

况且,使用8051系列单片机时,单片机资源也常有富余,CPU一般情况也只是空转,这就为它使用RTOS创造了条件。那么,使用RTOS的好处呢?

我举一个例子吧。假设我们编一个串行通讯程序,通讯协议如下:数据包长度为NBYTE,起始字节为STARTBYTE1,STARTBYTE2,最后一个字节为检验和,中间字节不可能出现连续出现STARTBYTE1,STARTBYTE2。

第一种方法,在中断中处理协议:

unsigned char Buf[NBYTE-2];
bit GetRight = 0;
void comm(void) interrupt 4//"串行口中断"
{    
    static unsigned char Sum,Flag = 0,i;    
    unsigned char temp;    if(RI == 1)    
    {        
        RI = 0;        
        temp = SBUF;        
        switch(Flag)        
        {        
        case 0:            
            if(temp == STARTBYTE1)            
            {               
                 Flag = 1;            
             }            
             break;        
         case 1:             
             if(temp == STARTBYTE2)            
             {                
                 Sum = STARTBYTE1 + STARTBYTE2;                
                 i = 0;                
                 Flag = 2;                
                 break;            
             }            
             if(temp == STARTBYTE1) break;            
             Flag = 0;            
             break;        
         case 2:            
         if(temp == STARTBYTE1)           
         {                
             Flag = 3;                
             break;            
         }            
         Sum += temp;            
         if((i >= (NBYTE-3)) && Sum == 0)            
         {                
             GetRight = 1;                
             Flag = 0;                
             break;            
         }            
         Buf[i++] = temp;            
         break;        
         case 3:            
         if(temp == STARTBYTE2)            
         {                
             Sum = STARTBYTE1 + STARTBYTE2;                
             Flag = 2;                
             i = 0;                
             break;            
         }           
         Sum += STARTBYTE1;            
         if((i >= (NBYTE - 3))&&Sum == 0)            
         {                
             GetRight = 1;                
             Flag = 0;                
             break;            
         }            
         Buf[i++] = STARTBYTE1;            
         if(temp == STARTBYTE1)            
         {                
             break;             
         }            
         Sum += temp;            
         if((i >= (NBYTE-3))&&Sum == 0)            
         {                
             GetRight = 1;                
             Flag = 0;                
             break;            
         }            
         Buf[i++] = temp;            
         Flag = 2;            
         break;        
     }    
  }
}
第二种方法,使用队列中断函数:
void comm(void) interrupt 4//"串行口中断"
{    
    if(RI == 1)    
    {
          RI=0;        
          SBUF 入队;    
     }
}
主程序不断调用的函数:
unsigned char Buf[NBYTE-2];unsigned char ReadSerial(unsigned char *cp)
{    
    unsigned char i;    
    unsigned char temp,Sum;    
    temp = 队列中数据个数;    
    if(temp < (NBYTE)) return 0;
    
    出队 temp;    
    if(temp != STARTBYTE1) return 0;
    
    temp=队列首字节;     
    if(temp != STARTBYTE2) return 0;
    
    出队 temp;    
    sum = STARTBYTE1 + STARTBYTE2;    
    for(i = 0;i < NBYTE-3;i++)    
    {
        temp = 队列首字节;        
        if(temp == STARTBYTE1)        
        {            
            temp = 队列次首字节;            
            if(temp == STARTBYTE2) return 0;        
        }        
        出队 temp;        
        *cp++ = temp;        
        Sum += temp;    
    }    
    temp = 队列首字节;    
    Sum += temp;    
    if(Sum != 0) return 0;    
    出队 temp;    
    return 1;
}
第三种方法,使用RTOS中断函数:
void comm(void) interrupt 4//"串行口中断" 
 {     
     OS_INT_ENTER();     
     if(RI == 1)     
     {        
         RI = 0;        
         OSIntSendSignal(RECIVE_TASK_ID);     
     }    
     OSIntExit();
}
ID为RECIVE_TASK_ID的任务.
void Recive(void) 
{     
    unsigned char temp,temp1,Sum,i;      
    OSWait(K_SIG,0);     
    temp = SBUF;     
    while(1)     
    {         
        while(1)         
        {             
            OSWait(K_SIG,0);             
            temp1 = SBUF;             
            if((temp == STARTBYTE1)&&(temp1 == STARTBYTE2)) break;             
            temp = temp1;         
        }         
        Sum = STARTBYTE1 + STARTBYTE2;         
        OSWait(K_SIG,0);         
        temp = SBUF;         
        for(i = 0;i < NBYTE-3;i++)         
        {             
            OSWait(K_SIG,0);             
            temp1 = SBUF;             
            if((temp == STARTBYTE1)&&(temp1 == STARTBYTE2))             
            {                 
                OSWait(K_SIG,0);                 
                temp=SBUF;                 
                i = -1;                 
                Sum = STARTBYTE1 + STARTBYTE2;                 
                continue;             
            }             
            Buf[i] = temp;             
            Sum += temp;             
            temp = temp1;         
        }         
        Sum += temp1;         
        if(Sum == 0) OSSendSignal(命令解释任务 ID);     
    }
}

以下为这几种方法的比较可读性和编程容易性方面

第三钟方法最好(如果允许使用goto语句,程序更加简单易读)

第二种次之(因为要编队列程序)

第一种相对较差,如果协议更加复杂,这方面更加明显。

程序简单易读,自然出错机会小了。

RAM占用方面,第三种方法较少,第二种最多(因为队列占用大量空间),第一种最少。

中断执行时间方面,第三种方法最长,第二种最短,第一种较长。

从功能方面,第三种方法最强,它还可以进行超时处理(虽然例子程序没有),其它方法均不行。

如果数据来的太快,命令处理程序来不及处理,三种方法处理方式不太一样,第一种和第三种方法类似:丢弃以前数据,第二种则是丢弃后到的数据。而且,第二种方法必须等命令处理程序完成后才处理下一个数据包,而第一种和第三种方只需命令处理程序将数据收取后就可处理下一个数据包。也就是说,第一种和第三种与命令处理程序并行处理,第二种方法为串行处理。

现在,一般情况下,开发的效率第一,执行的效率(包括执行时间和资源占用)第二。在这种情况下,降低些许效率换取开发的效率的较大提高,何乐而不为?

何况,单个模块的执行的效率高不等于整个程序执行效率高。例如,如果程序需要等待一段时间,一般用程序延时或定时器延时。无论何种方法,CPU不再处理其它工作,效率很低。而用RTOS,等待的时候CPU可以处理其它工作,效率得到提高。

以下摘自《uC/OS-II--源码公开的实时嵌入式操作系统》:

“实时内核也称为实时操作系统或RTOS。使用它使得实时应用程序的设计和扩展变得容易。不需要大的改动就可以增加新的功能。通过应用程序分割为若干独立的任务,RTOS使得应用程序的设计过程大为简化。使用可剥夺性的内核时,所有时间要求苛刻的事件都得到了尽可能快捷、有效的处理。通过有效的服务;如信号量、邮箱、队列、延时、超时等;RTOS使得资源得到更好的利用。“如果应用项目对额外的需求可以承受,应该考虑使用实时内核。这些额外的需求是:内核的价格,额外ROM/RAM开销,2至4百分点的CPU额外负担。“还有没提到的一个因素是使用实时内核增加的价格成本。在一些应用中,价格就是一切,以至于对使用RTOS连想都不敢想。”

总而言之,适用的就是最好的,不要拒绝RTOS,在它适用的情况下,它工作得很好。

来源:STM32嵌入式开发

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 21

一、概述

本文应用主要是使用SPI驱动wiznet的网络接口芯片W5xxx,根据wiznet提供的ioLibrary_Driver库支持W5100、W5100S、W5200、W5300、W5500。主要从以下几点进行说明:

1、使用hpm_sdk的cmake框架对接wiznet的ioLibrary_Driver。

2、ioLibrary_Driver的底层读写相关操作接口注册对接。

3、以ioLibrary_Driver库测试wiznet的速率性能。

本文不做wiznet芯片相关寄存器阐述,只简单阐述W5xxx的通信机制。

本文以W5500作为本文测试芯片。

本文例子仓库分支地址:

https://github.com/RCSN/hpm_sdk_extra/tree/feature/add_spi_drive_wizchip...

1.png

二、流程

(一)使用hpm_sdk的cmake框架对接wiznet的ioLibrary_Driver

在一些开源项目上,可能并没有使用cmake来进行搭建,而hpm_sdk基于cmake构建,所以在移植的过程中,建议使用cmake框架加入开源项目。本文以对接wiznet的ioLibrary_Driver作为例子供开发者相关参考。

wiznet的ioLibrary_Driver仓库地址:

https://github.com/Wiznet/ioLibrary_Driver 

里面包含了w5xxx的驱动程序以及应用程序协议,使用该库基本可以很快能对接好相应的功能实现。

需要关心的是Ethernet文件夹,里面包含了如W5500,W5300,W5200,W5100,W5100S的读写相关中间层,还提供了socket应用层操作接口。

2.png



1、芯片读写操作层加入

在此库中,仅支持一款w5xxx,所以添加到工程,也只能是一个芯片读写操作的文件夹。在每个芯片上加入cmakelists文件,把对应的头文件包含以及添加对应的C文件。

如以下:比如W5100文件夹,使用sdk_inc和sdk_src命令,把该文件下的所需的文件进行添加以及包含该文件夹的头文件。

3.png

2、Ethernet文件加入

外层想使用文件夹的时候,可以通过set一个参数来判断对应的芯片操作选择,然后再添加ethernet内部的socket.c和config.c文件。

通过CONFIG_WIZNET参数来进行对不同芯片加入筛选,以及加入两个宏定义,一个是给config表示目前使用的是哪个芯片,一个是让app知道引用的是哪个头文件。


4.png

3、APP加入ioLibrary_Driver相关文件。

在本文中,使用的芯片是W5500,需要用到DHCP和ioLibrary_Driver里面application的loopback的tcp client接口,那么在app的camkelist可以做以下操作:

(1)set(CONFIG_WIZNET "w5500") 设置CONFIG_WIZNET为w5500,这样cmake到ioLibrary_Driver索引时候只添加w5500这个文件夹。

(2)add_subdirectory(ioLibrary_Driver)  加入ioLibrary_Driver文件目录

(3)sdk_app_src sdk_inc 加入ioLibrary_Driver需要的相关文件和头文件


5.png

4、使用sdk_env的start_gui生成工程或者使用命令行生成都可以。这里使用start_gui


6.png

可以看到ioLibrary_Drivers对应的目录和文件都已经添加到SES。


7.png

如此编译也Ok


8.png

(二)ioLibrary_Driver的底层读写相关操作接口注册对接

在ioLibrary_Drivers的config上,需要接口注册的主要是临界区、CS片选操作、单字节收发、块收发传输接口。在该库中也有对应的注册接口wizchip_conf.h中。



9.png


10.png

1、临界区注册

这里临界主要做全局中断的使能和禁止,可根据自身应用添加

11.png

2、CS片选信号操作注册

在这里为了避免CS拉低后存在较高的间隔,实际的拉低在actual_cs_sel API操作,就是在开启SPI传输的时候调用,具体请详见 玩转先楫SPI外设系列 文章,这里不做阐述说明。

cs操作接口使用sdk对应的board的cs操作API,可自行根据应用更改。


12.png

3、读写单字节操作注册

需要注意的是,这里为了方便快速操作,比如在读接口SPI设置为只读,然后设置传输长度,复位FIFO,开启传输,等待数据接收完成后读取数据。写接口也是如此。具体请详见 玩转先楫SPI外设系列 文章,这里同样不做阐述说明。


13.png

4、块读写传输操作注册

DMA相关的操作请详见  玩转先楫SPI外设系列 文章,这里同样不做阐述说明。

5、注册相关操作接口


14.png

(三)以ioLibrary_Driver库测试wiznet的速率性能。

W5500官方手册中,虽然可以支持到80M的SPI SCLK,但官方的建议是33.3M推荐频率,再高的频率会出现些许采样问题,在实际测试的过程中也是如此,hpm_sdk的Board.h默认为20M,本文SLCK的频率调整为30M左右测试。

在性能上,wiznet官方也出了W5500对应的MAX速率为15Mbps。如果需要更高的可以选择W5100/s




15.png

16.png

17.png

在本文所实现的例子实现中,可通过cmakelists开启或者关闭DHCP,或者开启和关闭tcp iperf测试。默认都是开启。


18.png

如果想静态IP分配,那么直接在main.c中更改load_net_parameters API里面的ip值即可。


19.png

iperf测试软件链接: 

https://cloud.firebbs.cn/forum/201903/14/141408i20v8ny2i8tckkzy.zip?OSSA...

这里iperf测试主要测试W5500作为Tcp client的发送性能。在SPI SCLK 30M下,可以达到17Mbps速度性能,与wiznet官方提供的15Mbsps速率相当,满足性能需求。


20.png    

21.png

目前ioLibrary_Driver库性能还能继续提升,比如修改socket的send和recv实现逻辑,减少查询和等待;中间层少数据收发再次封装等等。

在逻辑分析仪下,socket的发送接口send,在实际发送前,会查询以及等待相关网络状态寄存器,占用了不少时间。wiznet提供的15mbps只有理论的一半性能也是这个时间所消耗引起。



22.png

三、总结

1、在ioLibrary_Driver库下,hpm的SPI驱动W5500的网络性能可以达到17Mbps,与wiznet官方的15Mbps保持一致。

2、如果需要提高性能,可以选择wetne其他芯片。或者自身优化wiznet的ioLibrary_Driver库。比如socket的发送send逻辑。

3、对于HPM6200,HPM5300等先楫通用MCU没有以太网接口,但是有实际以太网需求或者方案平替,可以选择SPI驱动相关的芯片网络接口。

来源:RCSN嵌入式

免责声明:本文为转载文章,转载此文目的在于传递更多信息,版权归原作者所有。本文所用视频、图片、文字如涉及作品版权问题,请联系小编进行处理(联系邮箱:cathy@eetrend.com)。

围观 20

页面

订阅 RSS - 单片机