周末日常来卷
一、电路相关
1 二极管篇
(1)暗恋就像根二极管,总是单向的电流。除非你运气好,表白时二极管反向击穿了,否则你就一直这样毫无回报的付出吧,别抱怨,谁让你选二极管呢。
(2)爱情里的背叛就像用来消除交越失真的那根二极管,刚知道时你怎么也想不明白为什么是那个人抢了你爱人,可是后来才明白,原来交流和直流是不一样的,所以,无论男人女人,在结婚以前,都别把你想托付一生的那个人介绍给你最好的哥们姐们,因为往往最后,问题就出在这里。
2 三极管篇
(1)三角恋就像三极管,总能把电路搞的不一样,三角恋也会把生活变得热闹,但是,毕竟,生活不是电路,还是别那么戏剧化的好。
(2)爱情就像是三极管,放大倍数越高的,越不稳定。
(3)人生就像三极管的输出特性曲线一样,有付出就有回报的永远只是饱和区里那一小块明显,大部分人都生活在放大区,当基极电流定下来后,由于存在着巨大的厄尔利电压,即使你在努力,起伏也不大,只能在那个小范围里慢慢前进有限的距离,等到啥时候时间把你带到击穿区,看似你飞升了,确实你也飞升了,直接飞到了天堂。所以,当你觉得委屈时,看看你脚下,还有很多更低的电压线呢。有的人连导通电压都没过呢,当你没房子时,想想非洲人民,连水都喝不上呢。
3 放大器篇
(1)初恋男生的心就像个最简单的信号放大器,女生控制着输入端。她一个最微小的快乐信号都能给他带来极大的幸福感,同样,她不小心的小伤害也会被他单纯的放大成无比伤痛。但是,男生不会一直这么蠢,当她和他越来越接近时,他慢慢就会给自己加上滤波器,当他们结婚后,他没准还会给她加上负反馈。
(2)热恋中的女生的心就像被加了一个差动放大器,男朋友的一切优点都被当成差模信号被放大,而他的缺点都被选择为共模信号抑制掉了。
(3)男生的心就像是三极管放大器,恋爱时是共基极的,她的付出总可以在他那得到几百倍的回报,但是结婚后,就改成了共集电极了,往往她的付出都是得不到等价的输出了,兴许过了七年之痒,没心肺的那部分男生还会变成共射极,这时候的输出虽然放大很多,却是反相了,和她的期望完全不同了。
(4)爱情就像功率放大器,失真小的电流周期长而且稳定,没什么激情,失真大的,导通角又小,只适合高频,不适合咱们低频。所以,只能折中一下,用个甲乙类放大器。所以最后,可能过一辈子的都是经济适用男和简单方便女。
(5)人生啊,就像放大器,无论多牛,都得有个接地端,所以,你这一生,总得有个归宿,老是飘着,虽然潇洒,但不是那么舒服,客死异乡,总是件有点凄凉感觉的事,除非你把自己献给梦想了。
4 场效应管篇
(1)如果你爱一个男生,非常笃定的话就和他结婚吧,男生的爱情就像绝缘栅场效应管,一般不可以测,所以,你千万别用你的闺蜜好友啥的来试他,男生的防线就如模电老师形容那管子一样,一测试就坏,而且,就算坏了,你还不知道他是什么时候坏的。就像你不知道男生什么时候变心一样。
(2)人生就像绝缘栅场效应管,虽然已经很小心的使用了,可是,还是不知道什么时候就会在没想到的因素中挂了。
5 电桥篇
爱情就像电桥一样,需要沟通,当无法沟通时,想方设法也要沟通,面对面永远好于背对背。因为造成爱情失败的本质原因往往不是缺乏了解,而是理解错了。只有沟通了,才能知道对方与自己到底需要什么。
6 PN结篇
(1)生活就像PN结一样,怎么造都会有电容影响,生活也都会有坎坷与不顺。你希望生活顺心如意,希望爱情一直甜蜜,希望婚姻幸福,对不起,这和消除PN结电容一样,是个世界性难题,当然也需要我们发现和探索。
(2)爱情就是维系男女的PN结,老师说PN结改变了这个世界。同样,这个世界里爱情也创造着它的奇迹。爱情是文明的产物,PN结也是,爱情里需要一个男人与一个女人,PN结也需要两个不同的半导体。人类不能没有爱情就像这个时代不能没有PN结一样,PN结主导了电子世界,爱情主导了我们的文明历史。
7 集成运放篇
(1)人生就像双极型集成运放F007,虽然很经典,但却要被更好的替代了,就像那些历史人物,那些过去的生活,虽然很精彩,可是也只能放在课本里做教材,作为后人学习之用。长江后浪推前浪,前浪死在课本上。
(2)人生就像集成运放,总有些人是来提供社会前进动力的,就像那些电源。
(3)人生就像集成运放,总是很难找到电流走向,就像你总是会迷茫一样。总是说要注重过程,可是大多数时候要用集成运放的人只关心结果。
总结 whaosoft aiot http://143ai.com
模电和爱情一样,都很难懂,但不同的是,不懂模电,是挂科,失去的是奖学金,爱情你要是没懂,那就得失去一个人了。你要是把模电搞的很懂,你可以考个高分,可是,你要是把爱情搞的很懂,估计只能出家了。所以生活中对待某些事该认真的时候认真,有时候马虎一点也没什么不好。
二、电路板GND与外壳GND之间,接一个电阻一个电容会更好
外壳是金属的,中间是一个螺丝孔,也就是跟大地连接起来了。这里通过一个1M的电阻跟一个0.1uF的电容并联,跟电路板的地连接在一起,这样有什么好处呢?
-
外壳地如果不稳定或者有静电之类的,如果与电路板地直接连接,就会打坏电路板芯片,加入电容,就能把低频高压,静电之类的隔离起来,保护电路板。电路高频干扰之类的会被电容直接接外壳,起到了隔直通交的功能。
-
那为什么又加一个1M的电阻呢?这是因为,如果没有这个电阻,电路板内有静电的时候,与大地连接的0.1uF的电容是隔断了与外壳大地的连接,也就是悬空的。这些电荷积累到一定程度,就会出问题,必须要与大地连接才行,所以这里的电阻用于放电。
-
1M的电阻这么大,如果外面有静电,高压之类的,也能有效降低电流,不会对电路内的芯片造成损坏。
三、嵌入式设备的通信协议特点
为嵌入式设备而设计的通信协议,通常有如下三种:
考虑到嵌入式设备的内存、算力有限,固定二进制是首选通信协议。
下文简析嵌入式设备通信协议应该有的特点。
简单性
保证协议是一个简单的方案,晦涩难懂往往意味着实现困难和容易出错。协议的结构宜采用平面方式,每个域作用明确,数据域尽可能设计得长度和位置固定,注释详尽,文档清晰,实例丰富,让人尽快上手和理解。
协议一般都需要以下域:帧头,长度,帧类型,目标地址,源地址,数据,校验,帧尾。
串口通信数据包格式如下图所示。
可扩展
必须保证将来增加功能和更改硬件后协议仍能胜任工作,这往往是通过预留空间来实现,协议的变更应该只是量的增加,不至于引起协议结构的变化。
低耦合
理想情况下每个协议包是原子信息,即本协议包不与其他协议包牵连,以防止通讯丢帧和设置牵连带来的错误。
稳定性
协议包长度适宜:太小包含的信息过少,协议包的种类繁多,容易引起通讯混乱和牵连错误;太大包含的信息过多,可读性较差,组帧和解帧的工作困难,还会带来通讯易受干扰的缺陷,一般协议长度以最小原子性信息为标尺。
协议必须包括校验机制,以便于接收方判别协议包正确完整接收,如果出错需要较好的机制来确保通讯成功(如重传)。
高效率
按信息类型区分协议包类别,如:设置网络信息参数,设置当前运行参数,可以区分开来,方便程序处理。
将同种操作编码为一个子集是一种高效手段,如Read操作,编码为0x0010,Write操作,编码为0x0020。
数据尽可能设计成同构模式,如果实在有差异,至少将同类型数据放置在一起,这样程序可以充分利用指针和线性寻址加速处理。
易实现
尽量减少复杂算法的使用,如,通讯链路稳定,数据帧的校验码可以由CheckSum代替CRC。除非资源非常紧张,否则不要将过多的信息挤压在一个数据里,因为它会带来可读性差和实现困难。
尽可能地让硬件ISR完成驱动工作,不要让“进程”参与复杂的时序逻辑,否则处理器将步履蹒跚且逻辑复杂!如:
接收固定长度的数据帧,可以使用DMA,每接收完一帧DMA_ISR向进程发消息。小心处理DMA断层异常(接收的数据帧长度正常但数据错误,数据为上帧的后半部分+本帧的前半部分)。
接收不定长的数据帧,可以使用状态机,当接收到“帧尾数据”时向进程发消息。小心数据紊乱和超时异常(数据紊乱时需要将状态机及时复位,超时一般使用定时器监控)。
兼容硬件
如果通信链路是高速总线(如SPORT可达100Mbps),一般设计成一帧产生一次中断,它通过长度触发的DMA来实现,需要将协议设计成固定长度,如附录A。它具备高效率,但灵活性较差。
如果通信链路是低速总线(如UART一般100kbps),一般接收一字节产生一次中断,可以将协议设计成变长帧,一个基于变长格式的UART通信协议实例:
它具备高灵活性,但效率较低。
上图显示了PC发送数据帧的格式,总长为64字节,是4字节的整倍数,符合绝大部分32位处理器结构体对齐的特性。
-
0x3C:INT8U,帧头,可见字符’<’
-
Len:INT8U,本帧的总数据长度,在图4即为64
-
Dst:INT8U,标识目标设备的ID号
-
Src:INT8U,标识源设备的ID号
-
Data:56字节的存储区,内容依赖于具体的通信帧(实例见表2)
-
Cmd:INT16U,数据帧的类别
-
CS:INT8U, 对它前面所有数据(62字节)进行8位累加和校验
-
0x7D:INT8U, 帧尾,可见字符’}’
Data域数据结构实例:
四、获取STM32代码运行时间
前言
测试代码的运行时间的两种方法:
-
使用单片机内部定时器,在待测程序段的开始启动定时器,在待测程序段的结尾关闭定时器。为了测量的准确性,要进行多次测量,并进行平均取值。
-
借助示波器的方法是:在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器通过检查高电平的时间长度,就知道了这段代码的运行时间。显然,借助于示波器的方法更为简便。
借助示波器方法的实例
Delay_us函数使用STM32系统滴答定时器实现:
#include "systick.h"
/* SystemFrequency / 1000 1ms中断一次
* SystemFrequency / 100000 10us中断一次
* SystemFrequency / 1000000 1us中断一次
*/
#define SYSTICKPERIOD 0.000001
#define SYSTICKFREQUENCY (1/SYSTICKPERIOD)
/**
* @brief 读取SysTick的状态位COUNTFLAG
* @param 无
* @retval The new state of USART_FLAG (SET or RESET).
*/
static FlagStatus SysTick_GetFlagStatus(void)
{
if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk)
{
return SET;
}
else
{
return RESET;
}
}
/**
* @brief 配置系统滴答定时器 SysTick
* @param 无
* @retval 1 = failed, 0 = successful
*/
uint32_t SysTick_Init(void)
{
/* 设置定时周期为1us */
if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY))
{
/* Capture error */
return (1);
}
/* 关闭滴答定时器且禁止中断 */
SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);
return (0);
}
/**
* @brief us延时程序,10us为一个单位
* @param
* @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
* @retval 无
*/
void Delay_us(__IO uint32_t nTime)
{
/* 清零计数器并使能滴答定时器 */
SysTick->VAL = 0;
SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk;
for( ; nTime > 0 ; nTime--)
{
/* 等待一个延时单位的结束 */
while(SysTick_GetFlagStatus() != SET);
}
/* 关闭滴答定时器 */
SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
}
检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
#ifndef __GPIO_H
#define __GPIO_H
#include "stm32f10x.h"
#define LOW 0
#define HIGH 1
/* 带参宏,可以像内联函数一样使用 */
#define TX(a) if (a) \
GPIO_SetBits(GPIOB,GPIO_Pin_0);\
else \
GPIO_ResetBits(GPIOB,GPIO_Pin_0)
void GPIO_Config(void);
#endif
#include "gpio.h"
/**
* @brief 初始化GPIO
* @param 无
* @retval 无
*/
void GPIO_Config(void)
{
/*定义一个GPIO_InitTypeDef类型的结构体*/
GPIO_InitTypeDef GPIO_InitStructure;
/*开启LED的外设时钟*/
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &GPIO_InitStructure);
}
在main函数中检验Delay_us的执行时间:
#include "systick.h"
#include "gpio.h"
/**
* @brief 主函数
* @param 无
* @retval 无
*/
int main(void)
{
GPIO_Config();
/* 配置SysTick定时周期为1us */
SysTick_Init();
for(;;)
{
TX(HIGH);
Delay_us(1);
TX(LOW);
Delay_us(100);
}
}
示波器的观察结果:
可见Delay_us(100),执行了大概102us,而Delay_us(1)执行了2.2us。
更改一下main函数的延时参数:
int main(void)
{
/* LED 端口初始化 */
GPIO_Config();
/* 配置SysTick定时周期为1us */
SysTick_Init();
for(;;)
{
TX(HIGH);
Delay_us(10);
TX(LOW);
Delay_us(100);
}
}
示波器的观察结果:
可见Delay_us(100),执行了大概101us,而Delay_us(10)执行了11.4us。
结论:此延时函数基本上还是可靠的。
使用定时器方法的实例
Delay_us函数使用STM32定时器2实现:
#include "timer.h"
/* SystemFrequency / 1000 1ms中断一次
* SystemFrequency / 100000 10us中断一次
* SystemFrequency / 1000000 1us中断一次
*/
#define SYSTICKPERIOD 0.000001
#define SYSTICKFREQUENCY (1/SYSTICKPERIOD)
/**
* @brief 定时器2的初始化,,定时周期1uS
* @param 无
* @retval 无
*/
void TIM2_Init(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
/*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/* Time base configuration */
TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;
TIM_TimeBaseStructure.TIM_Prescaler = 0;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
TIM_ARRPreloadConfig(TIM2, ENABLE);
/* 设置更新请求源只在计数器上溢或下溢时产生中断 */
TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
}
/**
* @brief us延时程序,10us为一个单位
* @param
* @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
* @retval 无
*/
void Delay_us(__IO uint32_t nTime)
{
/* 清零计数器并使能滴答定时器 */
TIM2->CNT = 0;
TIM_Cmd(TIM2, ENABLE);
for( ; nTime > 0 ; nTime--)
{
/* 等待一个延时单位的结束 */
while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
}
TIM_Cmd(TIM2, DISABLE);
}
在main函数中检验Delay_us的执行时间:
怎么去看检测结果呢?用调试的办法,打开调试界面后,将Time变量添加到Watch一栏中。然后全速运行程序,既可以看到Time中保存变量的变化情况,其中TimeWidthAvrage就是最终的结果。
可以看到TimeWidthAvrage的值等于0x119B8,十进制数对应72120,滴答定时器的一个滴答为1/72M(s),所以Delay_us(1000)的执行时间就是72120*1/72M (s) = 0.001001s,也就是1ms。验证成功。
备注:定时器方法输出检测结果有待改善,你可以把得到的TimeWidthAvrage转换成时间(以us、ms、s)为单位,然后通过串口打印出来,不过这部分工作对于经常使用调试的人员来说也可有可无。
两种方法对比
软件测试方法
操作起来复杂,由于在原代码基础上增加了测试代码,可能会影响到原代码的工作,测试可靠性相对较低。由于使用32位的变量保存systick的计数次数,计时的最大长度可以达到2^32/72M = 59.65 s。
示波器方法
操作简单,在原代码基础上几乎没有增加代码,测试可靠性很高。由于示波器的显示能力有限,超过1s以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。