LED
理解PWM需要知道的知识
(1)脉冲
解释:电子设备中电平状态发生的突变,通常突变时间很短,突变后极短时间后重新变为为原来的电平状态.(突变状态很短,两次突变间的时间相对较长)
(2)脉冲循环
解释:可以理解为一次突变到下一次突变所花的时间如下图:
(3)*(重点)占空比
解释:一个脉冲循环内通电时间所占的比例.,如下图:
举个例子:脉冲宽度1μs,信号周期5μs的脉冲序列即t=1,T=5,经过公式-占空比=t/T可以得到占空比为0.2.
(4)滤波器
解释:滤波器的组成为电感,电容,电阻等元器件.虽然PWM能通过通过改变占空比的方法.使电压的平均值达到稳压值,但输出稳定电压是靠PWM之后接的的滤波器来实现的。
(5)平均电压/输出电压
解释:
平均电压电压在一个周期T内积分之后再除以T.
也可以等同于写成:
输出电压 = (接通时间 / 脉冲时间)* 最大电压值
计算方式(平均电压)的示意图如下:
PWM的定义
PWN(Pulse-width modulation)的中文名是脉冲宽度调制.那么我们来看一下wikipedia对它的定义:
脉冲宽度调制(英语:Pulse Width Modulation,缩写:PWM),简称脉宽调制,是将模拟信号变换为脉冲的一种技术,一般变换后脉冲的周期固定,但脉冲的占空比会依模拟信号的大小而改变.在模拟电路中,模拟信号的值可以连续进行变化,在时间和值的幅度上都几乎没有限制,基本上可以取任何实数值,输入与输出也呈线性变化。所以在模拟电路中,电压和电流可直接用来进行控制对象,例如家用电器设备中的音量开关控制、采用卤素灯泡灯具的亮度控制等等 ...
计算PWN等效电压
PWM的等效电压计算公式为:
(此处我认为因为是方波所以可以将其视作平均电压)
U =(T1*Umax)/(T1+T2)
T1:导通时间
T2:断流时间
T1+T2 脉冲周期
Umax:电压幅值
所以根据公式可知,由于T1/(T1+T2)正是空占比,所以改变空占比就等于改变了等效电压,所以使得灯泡的亮度发生了变化
为什么Analogwrite的值是0-255?
LED亮度通过调节LED驱动器的PWM占空比来对亮度控制,一个PWM周期可以划分成2的控制位的次方个时钟周期而对大部分LED而言,控制位通常是8位,所以8位PWM能够提供256个亮度级的电平,因此PWM周期由256个时钟周期组成.
脉冲周期/频率和人眼的关系
LED的典型时钟频率是32kHz,那么根据公式PWM周期为256/32kHz=8ms.那么这样对于人眼而言这个闪烁频率很安全的避免了人眼能够觉察的闪烁.
在ARDUINO中使用PWM控制LED灯模拟呼吸灯的实验
实验准备:
实验主设备: Arduino UNO R3(图片来自NRIOBOT)
其他:
LED灯(若干)
面包板(一块)
杜邦线(双头公若干)
电阻(若干)(可选择/非必需)
连接图示意(通过Fritzing软件制作的简易电路图)
实验代码:
/*先要介绍一下analogwrite的用法
将模拟值(PWM波)输出到管脚。可用于在不同的光线亮度调节发光二极管亮度或以不同的速度驱动马达。调用analogWrite()后,该引脚将产生一个指定占空比的稳定方波,直到下一次调用analogWrite()(或在同一引脚调用digitalRead()或digitalWrite())
这种方法也叫快速PWM方式*/
需要上传到ARDUINO中的代码:
//设定使用9号口
void setup (){
pinMode(9,OUTPUT);
}
void loop(){
//由于上文中提到的所以为256种亮度
for (int a=0; a<=255;a++) //控制PWM亮度的增加
{
analogWrite(9,a);
delay(8);
}
for (int a=255; a>=0;a--) //控制PWM亮度减小
{
analogWrite(9,a);
delay(8);
}
delay(300); //完成一个循环
}
Analogwrite和占空比的关系
analogwrite(x,y)
X是管脚,而y(value)就是亮度级(在LED中)
占空比的计算方法就是: 占空比=y/256
对于Analogwrite占空比的一个特殊之处的解释
对于快速PWM模式,如果我们代码用了analogWrite(9, 0)即Y(value)=0,实际上应该有1/256的占空比,然而实际输出的电平为0.这是因为在Arduino的强制设定,当检测到AnalogWrite的value为0,那么就等于关闭了PWM.所以带来的问题是,如果我们设置analogWrite(9, 1),那么占空比2/256,所以在0到1之间产生了一个跳跃,丢弃了占空比为1/256的情况.
总结
这次的python实验中,让我们尝试了怎么使用Arduino和LED灯做出呼吸灯的效果,因为对于机器是怎么输出高电平(5v)和低电平(0v)之间的电压好奇,所以探究了一下原理,总结来说就是机器通过pwm在管脚产生了一定占空比的方波,改变空占比就等同于改变了等效电压,所以使得灯泡的亮度发生了变化.
转自: xlxw<-博客园/a>
作者:张仕宗
LED数码管在单片机系统中应用非常普遍,是由发光二极管构成的。数码管由7个发光二极管组成的一个“日”字形,如果需要显示小数点,那么就再加上一个点,就是8段数码管。
数码管显示亮度高,相应速度快,分共阴极和共阳极两种形式,常用的有单个的和4联的,还有两联的和专门用来显示时间的。
/***************************************************
*程序功能:点亮一个led数码管,让它显示数字从0到9 *
*日期:2015.5.11 *
****************************************************/
#include
#define LONG 50000
#define SHORT 10000
//先定义字形码,table数组中装下了自形0到9
unsigned int table[] = {0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x90};
//延时函数
void delay(int i) {
while(i--) {
}
}
void main() {
int i = 0;
while(1) {
for(i = 0; i < 10; i++) {
P0 = table[i];
delay(LONG);
}
}
}
文章来源:极客头条
1、前言
在软件开发的过程中,debug(调试)是一个很重要的事情,因为没有百分之百正确的代码,一旦结果不符合预期,我们需要知道问题出在哪里了。
在PC环境下开发应用程序,我们不需要太操心,因为有各式各样的模拟器、调试器可供使用,我们可以追踪到每一行代码的执行过程和执行结果,找出问题只是时间问题而已。但在嵌入式环境下,就有些麻烦了,能用的手段,无外乎两种:
1)使用硬件仿真器定位问题。
2)使用日志输出定位问题。
对嵌入式工程师(特别是linux工程师)而言,鉴于使用硬件仿真器的诸多不便(成本高,无法保证人手一个;硬件连接复杂,需要预留特定接口;使用不方便;等等),日志输出几乎成为必备且唯一的debug手段。但是,总会有例外:
系统刚刚启动,在日志输出的通道(通常是UART接口)ready之前,怎么debug?
在不得不使用仿真器之前,我们还有一个简单的方法,就是点LED灯,本文将结合“X Project”“【任务2】启动到u-boot command line”实现的过程,对这个方法进行简单的介绍和总结。
2、 思路
从本质上讲,软件debug最高效的手段,是良好的设计、优秀的编码、细致的代码检查,当然,百密总有一疏。但大多数时候,我们只要能找到出现问题的大概位置,再辅以代码的检查,就可以百分之九十九的解决问题,这也是通过日志输出的方法定位问题的基本逻辑。
因此,在日志输出通道ready之前,我们也可以使用同样的思路,借助LED灯,实现问题的定位。大致思路如下:
1)如果系统只有一个LED灯,可以在代码中,正确执行某一个步骤之后,点亮LED,以此类推,一步一步检查代码执行的正确与否(其实“X Project”“【任务1】启动过程-Boot from USB”就是借用了这个思路)。
2)当然,如果系统有多个LED,例如四个,我们可以将这四个LED编码成一个4bit的数字(0~15),这样就可以表示更多的状态,下面是“X Project”基于bubblegum-96平台,实现的一个简单的接口(代码逻辑很简单,我就不解释了):
/* https://github.com/wowotechX/u-boot/blob/x_integration/board/actions/bub... */
/*
* A simple debug function for early debug, in which,
* we use four LEDs to display sixteen debug codes, from 0 to 15.
* Using it, we can know, at least roughly, at where out code is run.
*/
void bubblegum_early_debug(int debug_code)
{
uint8_t val;
val = debug_code & 0x1;
setbits_le32(GPIOA_OUTEN, 1 << DEBUG_LED0_GPIO);
clrsetbits_le32(GPIOA_OUTDAT, 1 << DEBUG_LED0_GPIO,
val << DEBUG_LED0_GPIO);
val = (debug_code >> 1) & 0x1;
setbits_le32(GPIOA_OUTEN, 1 << DEBUG_LED1_GPIO);
clrsetbits_le32(GPIOA_OUTDAT, 1 << DEBUG_LED1_GPIO,
val << DEBUG_LED1_GPIO);
val = (debug_code >> 2) & 0x1;
setbits_le32(GPIOF_OUTEN, 1 << DEBUG_LED2_GPIO);
clrsetbits_le32(GPIOF_OUTDAT, 1 << DEBUG_LED2_GPIO,
val << DEBUG_LED2_GPIO);
val = (debug_code >> 3) & 0x1;
setbits_le32(GPIOF_OUTEN, 1 << DEBUG_LED3_GPIO);
clrsetbits_le32(GPIOF_OUTDAT, 1 << DEBUG_LED3_GPIO,
val << DEBUG_LED3_GPIO);
}
3、一个例子
利用上面的debug接口,在调试串口驱动的时候,发挥了很大的作用,如下:
/* https://github.com/wowotechX/u-boot/blob/x_integration/drivers/serial/se... */
extern void bubblegum_early_debug(int debug_code);
static int owl_serial_probe(struct udevice *dev)
{
/* only for UART2, TODO */
bubblegum_early_debug(4);
/* pinmux */
setbits_le32(MFP_CTL2, 1 << 22);
bubblegum_early_debug(5);
/* device clock enable */
setbits_le32(CMU_DEVCLKEN1, 1 << 8);
bubblegum_early_debug(6);
/* reset de-assert */
setbits_le32(CMU_DEVRST1, 1 << 7);
bubblegum_early_debug(7);
/* set default baudrate and enable UART */
owl_serial_setbrg(dev, 115200);
/* enable uart */
setbits_le32(UART2_BASE + UART_CTL, UART_CTL_EN);
bubblegum_early_debug(8);
return 0;
}
串口驱动编写好之后,发现系统会死在owl_serial_probe中,于是添加了一系列的"点灯"操作,发现最后停留在“bubblegum_early_debug(5); ”上面(LED2和LED0亮),于是就可以确定这条语句出了问题:
setbits_le32(CMU_DEVCLKEN1, 1 << 8);
经过仔细检查,发现CMU_DEVCLKEN1寄存器定义错了(可能是抄Action的代码笔误了)。
4、总结
步骤很简单,之所以要写一篇文章,是想告诉大家,嵌入式开发其实挺简单的,只要有足够的细心和耐心,一切皆有可能。
作者:wowo
文章来源 :蜗窝科技