概述
一、呼吸灯的PWM原理
定时器生成PWM波
PWM全称是Pulse Width Modulation,通过控制高频信号的占空比,眼睛当成低通滤波器,可以控制亮暗。再循环更改PWM的阈值,就弄出了呼吸的效果
这里采用一个比较简单的方法生成PWM波:设置定时器中断然后根据阈值判断置高和置低。
whaosoft aiot http://143ai.com
void TIM3_IRQHandler(void)
{
TIM_ClearITPendingBit(TIM3,TIM_IT_Update);
if(counter==255)
counter = 0;
else
counter +=1;
if(mode == 0){
if(counter < pwm)
GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);
else
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1);
}
if(mode == 1)
{
if(counter < pwm)
GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);
else
GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2);
}
if(mode ==2){
if(counter < pwm)
GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0);
else
GPIO_ResetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0);
}
}
程序流程
-
开启外设时钟(GPIO和TIM)
void RCC_Configuration(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE);
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4|RCC_APB1Periph_TIM3, ENABLE);
}
-
配置GPIO
-
配置时钟, 使能中断(计数阈值,预分频,时钟分频,计数模式)
void tim3() //配置TIM3为基本定时器模式 ,约10us触发一次,触发频率约100kHz
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure
TIM_TimeBaseStructure. TIM_Period =9; //配置计数阈值为9,超过时,自动清零,并触发中断
TIM_TimeBaseStructure.TIM_Prescaler =71; // 时钟预分频值,除以多少
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频倍数
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数方式为向上计数
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 初始化tim3
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3溢出中断标志
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); // 使能TIM3的溢出更新中断
TIM_Cmd(TIM3,ENABLE); // 使能TIM3
}
-
配置中断优先级
void nvic() //配置中断优先级
{
NVIC_InitTypeDef NVIC_InitStructure; // // 命名一优先级变量
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 将优先级分组方式配置为group1,有2个抢占(打断)优先级,8个响应优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //该中断为TIM4溢出更新中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//打断优先级为1,在该组中为较低的,0优先级最高
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应优先级0,打断优先级一样时,0最高
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 设置使能
NVIC_Init(&NVIC_InitStructure); // 初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //要用同一个Group
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 溢出更新中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;// 打断优先级为1,与上一个相同,不希望中断相互打断对方
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 响应优先级1,低于上一个,当两个中断同时来时,上一个先执行
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
-
写中断服务函数
代码实现
为了方便按键检测,除了TIM3配置PWM波之外,TIM4用来检测是否有输入。由于使用开漏输出,这里使用5V电源。
#include "stm32f10x.h"
#include "math.h"
#include "stdio.h"
u8 counter=0;
int pwm=100;
int flag=0;
int mode =0;
int velocity =0;
int turning=1;
void RCC_Configuration(void); //时钟初始化,开启外设时钟
void GPIO_Configuration(void); //IO口初始化,配置其功能
void tim3(void); //定时器tim4初始化配置
void tim4(void); //定时器tim4初始化配置
void nvic(void); //中断优先级等配置
void exti(void); //外部中断配置
void delay_nus(u32); //72M时钟下,约延时us
void delay_nms(u32); //72M时钟下,约延时ms
void breathing(int velocity){
switch(velocity){
case 0:
if(flag)
pwm +=1;
if(pwm>240) flag=0;
if(flag == 0){
pwm -=1;
if(pwm<10) flag=1;
}
break;
case 1:
if(flag)
pwm +=2;
if(pwm>240) flag=0;
if(flag == 0){
pwm -=2;
if(pwm<10) flag=1;
}
break;
case 2:
if(flag)
pwm +=3;
if(pwm>240) flag=0;
if(flag == 0){
pwm -=3;
if(pwm<10) flag=1;
}
break;
}
}
void assert_failed(uint8_t* file, uint32_t line)
{
printf("Wrong parameters value: file %s on line %drn", file, line);
while(1);
}
void TIM4_IRQHandler(void) //TIM4的溢出更新中断响应函数 ,读取按键输入值,根据输入控制pwm波占空比
{
u8 key_in1=0x01,key_in2=0x01;
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);// 清空TIM4溢出中断响应函数标志位
key_in1= GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_12); // 读PC12的状态
key_in2= GPIO_ReadInputDataBit(GPIOC,GPIO_Pin_13);// 读PC13的状态
if(key_in1 && key_in2) turning =1;
breathing(velocity);
if(key_in1==0 && turning){
turning =0;
velocity = (velocity + 1) % 3;
}//调速度
if(key_in2==0 && turning){
turning =0;
mode = (mode + 1) % 3;
}//调颜色
}
void TIM3_IRQHandler(void) // //TIM3的溢出更新中断响应函数,产生pwm波
{
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); // // 清空TIM3溢出中断响应函数标志位
if(counter==255) //counter 从0到255累加循环计数,每进一次中断,counter加一
counter = 0;
else
counter +=1;
if(mode == 0){
if(counter < pwm) //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低
GPIO_SetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1); //将PC14 PC15置为高电平
else
GPIO_ResetBits(GPIOA,GPIO_Pin_0|GPIO_Pin_1); // 将PC14 PC15置为低电平
}
if(mode == 1)
{
if(counter < pwm) //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低
GPIO_SetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2); //将PC14 PC15置为高电平
else
GPIO_ResetBits(GPIOA,GPIO_Pin_1|GPIO_Pin_2); // 将PC14 PC15置为低电平
}
if(mode ==2){
if(counter < pwm) //当counter值小于pwm值时,将IO口设为高;当counter值大于等于pwm时,将IO口置低
GPIO_SetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0); //将PC14 PC15置为高电平
else
GPIO_ResetBits(GPIOA,GPIO_Pin_2|GPIO_Pin_0); // 将PC14 PC15置为低电平
}
}
int main(void)
{
RCC_Configuration();
GPIO_Configuration();
tim4();
tim3();
nvic();
while(1)
{
}
}
void delay_nus(u32 n) //72M时钟下,约延时us
{
u8 i;
while(n--)
{
i=7;
while(i--);
}
}
void delay_nms(u32 n) //72M时钟下,约延时ms
{
while(n--)
delay_nus(1000);
}
void RCC_Configuration(void) //使用任何一个外设时,务必开启其相应的时钟
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOC|RCC_APB2Periph_GPIOA|RCC_APB2Periph_AFIO, ENABLE); //使能APB2控制外设的时钟,包括GPIOC, 功能复用时钟AFIO等,
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4|RCC_APB1Periph_TIM3, ENABLE); //使能APB1控制外设的时钟,定时器tim3、4,其他外设详见手册
}
void GPIO_Configuration(void) //使用某io口输入输出时,请务必对其初始化配置
{
GPIO_InitTypeDef GPIO_InitStructure; //定义格式为GPIO_InitTypeDef的结构体的名字为GPIO_InitStructure
//typedef struct { u16 GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_Mode; } GPIO_InitTypeDef;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU; //配置IO口的工作模式为上拉输入(该io口内部外接电阻到电源)
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //配置IO口最高的输出速率为50M
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12|GPIO_Pin_13; //配置被选中的管脚,|表示同时被选中
GPIO_Init(GPIOC, &GPIO_InitStructure); //初始化GPIOC的相应IO口为上述配置,用于按键检测
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_OD; //配置IO口工作模式为 推挽输出(有较强的输出能力)
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //配置IO口最高的输出速率为50M
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0|GPIO_Pin_1|GPIO_Pin_2; //配置被选的管脚,|表示同时被选中
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIOA的相应IO口为上述配置
GPIO_PinRemapConfig(GPIO_Remap_SWJ_JTAGDisable,ENABLE); //失能STM32 JTAG烧写功能,只能用SWD模式烧写,解放出PA15和PB中部分IO口
}
void tim4() //配置TIM4为基本定时器模式,约10ms触发一次,触发频率约100Hz
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure
TIM_TimeBaseStructure. TIM_Period =9999; // 配置计数阈值为9999,超过时,自动清零,并触发中断
TIM_TimeBaseStructure.TIM_Prescaler =71; // 时钟预分频值,除以多少
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频倍数
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数方式为向上计数
TIM_TimeBaseInit(TIM4, &TIM_TimeBaseStructure); // 初始化tim4
TIM_ClearITPendingBit(TIM4,TIM_IT_Update); //清除TIM4溢出中断标志
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); // 使能TIM4的溢出更新中断
TIM_Cmd(TIM4,ENABLE); // 使能TIM4
}
void tim3() //配置TIM3为基本定时器模式 ,约10us触发一次,触发频率约100kHz
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; //定义格式为TIM_TimeBaseInitTypeDef的结构体的名字为TIM_TimeBaseStructure
TIM_TimeBaseStructure. TIM_Period =9; //配置计数阈值为9,超过时,自动清零,并触发中断
TIM_TimeBaseStructure.TIM_Prescaler =71; // 时钟预分频值,除以多少
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 时钟分频倍数
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // 计数方式为向上计数
TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); // 初始化tim3
TIM_ClearITPendingBit(TIM3,TIM_IT_Update); //清除TIM3溢出中断标志
TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); // 使能TIM3的溢出更新中断
TIM_Cmd(TIM3,ENABLE); // 使能TIM3
}
void nvic() //配置中断优先级
{
NVIC_InitTypeDef NVIC_InitStructure; // // 命名一优先级变量
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); // 将优先级分组方式配置为group1,有2个抢占(打断)优先级,8个响应优先级
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn; //该中断为TIM4溢出更新中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//打断优先级为1,在该组中为较低的,0优先级最高
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应优先级0,打断优先级一样时,0最高
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 设置使能
NVIC_Init(&NVIC_InitStructure); // 初始化
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1); //要用同一个Group
NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn; //TIM3 溢出更新中断
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;// 打断优先级为1,与上一个相同,不希望中断相互打断对方
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 响应优先级1,低于上一个,当两个中断同时来时,上一个先执行
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
二、早期555定时器芯片
由模拟 IC 奇才 Hans Camenzind 设计的 555 被称为有史以来最伟大的芯片之一。据说是世界上最畅销的集成电路,已售出数十亿。
一个带有 Signetics 标志的 8 针 555 定时器。它没有 555 标签,而是标有“52B 01003”和 7304 日期代码,表示 1973 年的第 4 周。
乏味地打磨环氧树脂封装以露出芯片(下图),并确定芯片是 555 定时器。Signetics 在 1972 年年中发布了 555 定时器,下面的芯片有一个 1973 年 1 月的日期代码(7304),所以它一定是最早的 555 定时器之一。奇怪的是,它没有标为 555,所以它可能是原型或内部版本。
我拍摄了详细的模具照片,在这篇博文中进行了讨论。
555 定时器的封装被打磨,露出硅芯片,中间的小方块。
555 定时器有数百种应用,从定时器或锁存器到压控振荡器或调制器的任何操作。下图说明了 555 定时器如何作为一个简单的振荡器工作。在 555 芯片内部,三个电阻形成一个分压器,产生 1/3 和 2/3 的电源电压的参考电压。外部电容器将在这些限制之间充电和放电,从而产生振荡。更详细地说,电容器将通过外部电阻器缓慢充电 (A),直到其电压达到 2/3 参考电压。在该点 (B),上(阈值)比较器关闭触发器并关闭输出。这会打开放电晶体管,使电容器 (C) 缓慢放电。当电容器上的电压达到 1/3 参考电压 (D) 时,较低(触发)比较器打开,设置触发器和输出,循环重复。电阻器和电容器的值控制时间,从微秒到几小时。
显示 555 定时器如何作为振荡器工作的图表。在 555 定时器的控制下,外部电容器通过外部电阻器进行充电和放电。
总而言之,555 定时器的关键组件是检测电压上限和下限的比较器、设置这些限制的三电阻分压器以及跟踪电路是充电还是放电的触发器。555 定时器还有两个我上面没有提到的引脚(复位和控制电压),它们可用于更复杂的电路。
从显微镜图像的合成中创建了下面的照片。在硅的顶部,一层薄薄的金属连接芯片的不同部分。这种金属在照片中以浅色痕迹清晰可见。在金属下方,一层薄薄的玻璃状二氧化硅层在金属和硅之间提供绝缘,除了二氧化硅中的接触孔允许金属连接到硅的地方。在芯片的边缘,细线将金属焊盘连接到芯片的外部引脚。
如上,555 计时器的模具照片。
芯片上不同类型的硅更难看到。芯片的区域用杂质处理(掺杂)以改变硅的电特性。N 型硅具有过量的电子(负),而 P 型硅缺乏电子(正)。在照片中,这些区域显示为略有不同的颜色,周围有细黑色边框。这些区域是芯片的组成部分,形成晶体管和电阻器。在windows中,保存的时候先另存在桌面,再拖进去覆盖即可!
晶体管是芯片中的关键元件。555 定时器使用 NPN 和 PNP 双极晶体管。如果您研究过电子学,您可能已经看过如下图所示的 NPN 晶体管图,显示了晶体管的集电极 (C)、基极 (B) 和发射极 (E),晶体管被图示为P硅夹在两个对称的N硅层之间,NPN 层构成 NPN 晶体管。事实证明,芯片上的晶体管看起来不像这样,而且基极通常甚至不在中间!
如上,NPN 晶体管的原理图符号,以及其内部结构的简化图。
下面的照片显示了 555 中的一个晶体管的特写,因为它出现在芯片上。硅中稍有不同的色调表明已掺杂形成 N 和 P 区域的区域。白色区域是硅顶部芯片的金属层 - 这些形成连接到集电极、发射极和基极的导线。
如上图,裸片上 NPN 晶体管的结构。
照片下方是一个横截面图,说明了晶体管的构造方式。除了你在书中看到的 NPN 之外,还有很多其他东西,但如果你仔细观察“E”下方的垂直横截面,你会发现形成晶体管的 NPN。发射极 (E) 线连接到 N+ 硅。其下方是连接到基极触点 (B) 的 P 层。在其下方是(间接)连接到收集器(C)的 N+ 层。6 晶体管被 P+ 环包围,将其与相邻组件隔离。
在IC内部的PNP晶体管:
如上图,555定时器芯片中的PNP晶体管。标注了集电极(C)、发射极(E)和基极(B)的连接,以及N和P掺杂硅。基极围绕发射极形成一个环,集电极围绕基极形成一个环。
555中的输出晶体管比其他晶体管大得多,并且具有不同的结构,以产生高电流输出。下面的照片显示了输出晶体管之一。注意被大集电极包围的发射极和基极的多个互锁“手指”。
如上图,555定时器芯片中的大电流NPN输出晶体管。集电极(C)、基极(B)和发射极(E)被标记。
电阻器是如何在硅中实现的?
电阻器是模拟芯片的关键部件。不幸的是,IC 中的电阻器很大且不准确。不同芯片的电阻可能相差 50%。因此,模拟 IC 的设计只有电阻的比率很重要,而不是绝对值,因为比率几乎保持不变。
如上,555定时器内部的电阻。电阻器是两个金属触点之间的一条 P 硅。
上面的照片显示了 555 中的一个 10KΩ 电阻器,它由一条 P 硅(粉灰色)形成,在两端与金属线接触。其他金属线穿过电阻器。电阻器具有螺旋形状,以使其长度适合可用空间。下面的电阻是一个 100KΩ 的夹点电阻。夹层电阻器顶部的 N 硅层使导电区域更薄(即夹住它),形成更高但不太准确的电阻。
555定时器内部的收缩电阻器。电阻器是两个金属触点之间的一条P硅。顶部的N层夹住电阻,增加电阻。垂直金属线穿过该电阻器。
有一些子电路在模拟 IC 中很常见,但起初可能看起来很神秘。电流镜就是其中之一。如果您看过模拟 IC 框图,您可能已经看到下面的符号,指示电流源,并想知道电流源是什么以及为什么要使用它。这个想法是你从一个已知的电流开始,然后你可以用一个简单的晶体管电路,电流镜“克隆”电流的多个副本。
如上图,电流源的原理图符号。
以下电路显示了如何用两个相同的晶体管实现电流镜。参考电流流经右侧的晶体管。(在这种情况下,电流由电阻设定。)由于两个晶体管具有相同的发射极电压和基极电压,它们产生相同的电流,因此右边的电流与左边的参考电流相匹配。
如上,电流镜电路,右边的电流复制左边的电流。
电流镜的一个常见用途是替换电阻器。如前所述,IC 内部的电阻器既大又不准确,不便之处。尽可能使用电流镜而不是电阻器来节省空间。此外,与两个电阻器产生的电流不同,电流镜产生的电流几乎相同。
如上图,三个晶体管在555定时器芯片中形成一个电流镜。它们共用同一个基极,两个晶体管共用发射极。
上述三个晶体管构成一个具有两个输出的电流镜。注意,三个晶体管共享基极连接,连接到右边的集电极,右边的发射极连接在一起。在原理图中,右侧的两个晶体管被绘制为单个双集电极晶体管Q19。
要了解的第二个重要电路是差分对,它是模拟 IC 中最常见的双晶体管子电路。 您可能想知道比较器如何比较两个电压,或者运算放大器如何减去两个电压。这是差分对的工作。
如上,简单差分对电路原理图。电流源通过差分对发送固定电流I。如果两个输入相等,则电流均分。
上面的示意图显示了一个简单的差分对。底部的电流源提供固定电流 I,该电流在两个输入晶体管之间分配。如果输入电压相等,则电流将平均分成两个分支(I1 和 I2)。如果其中一个输入电压比另一个高一点,则相应的晶体管会以指数方式传导更多的电流,因此一个分支获得更多电流,而另一个分支获得更少。一个小的输入差异足以将大部分电流引导到“获胜”分支,从而打开或关闭比较器。555 芯片使用一个差分对作为阈值比较器,另一对作为触发比较器。
最后
以上就是外向泥猴桃为你收集整理的嵌入式分享合集50的全部内容,希望文章能够帮你解决嵌入式分享合集50所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复