匿名
未登录
登录
百问网嵌入式Linux wiki
搜索
查看“第022课 传感器”的源代码
来自百问网嵌入式Linux wiki
名字空间
页面
讨论
更多
更多
页面选项
Read
查看源代码
历史
←
第022课 传感器
因为以下原因,您没有权限编辑本页:
您所请求的操作仅限于该用户组的用户使用:
用户
您可以查看与复制此页面的源代码。
=第001节_光敏电阻的使用= 这节课我们开始讲的传感器,有光敏电阻、DH11温湿度传感器、DS18B20温度传感器、HS0038红外接收器。 首先介绍光敏电阻传感器。 光敏电阻有一个特点,就是它的阻值随光照强度变化而变化, 看一下它的原理图,R5是一个普通电阻,LAS1是光敏电阻,它们串联组成一个分压电路, LAS1阻值变化,将会导致中间RES_AO测得的电压发生变化。 [[File:chapter22_lesson1_001.png|800px]] 这个电路图有点绕,画一个示意图如下: [[File:chapter22_lesson1_002.png|800px]] 使用ADC测量A点的电压,可以得知LAS1的变化情况,这里的测量是一个模拟信号。 现在假如需要这个光敏系统在光照大于/小于某个值时,发出中断,怎么办呢? 这里就需要再加一个比较的电路,B处的电压是可调电阻得到的电压,可以通过调节电阻进行变化。A、B两个电压最后接在一个比较器的“正负端”,当A>B时,输出1,反之输出0。 通过调节可调电阻,可以实现对比较阈值的控制。 现在这个电路就即可得到模拟信号和数字信号。 现在就可以开始写程序了,复制前面024的代码为025_sensors,这是这一章的第一个项目,再创建个001_photoresistor文件夹,将代码都放进去。 再创建一个sensors文件夹用来放本课的所有传感器代码,然后再创建photoresistor文件夹放本节课代码,再在里面创建photoresistor.c。 在代码里面,我们要做两件事: :: 1.启动ADC,读出AIN1电压值; :: 2.注册中断,当光线强度超过或小于某个阈值时,产生中断; 打开工程里的adc代码,原来的adc_init,只初始化了adc0,现在我们要用AIN1,修改下代码,传入个参数就能初始化对应的AIN: <syntaxhighlight lang="c" > void adc_init(int channel) { /* [15] : ECFLG, 1 = End of A/D conversion * [14] : PRSCEN, 1 = A/D converter prescaler enable * [13:6]: PRSCVL, adc clk = PCLK / (PRSCVL + 1) * [5:3] : SEL_MUX, 000 = AIN 0 * [2] : STDBM * [0] : 1 = A/D conversion starts and this bit is cleared after the startup. */ ADCCON = (1<<14) | (49<<6) | (channel<<3); ADCDLY = 0xff; } </syntaxhighlight> 后面的adc_read_ain0只能读取AIN0的数据,现在修改一下,传入个通道参数,就能读取对应通道的ADC值: <syntaxhighlight lang="c" > int adc_read(int channel) { adc_init(channel); /* 启动ADC */ ADCCON |= (1<<0); while (!(ADCCON & (1<<15))); /* 等待ADC结束 */ return ADCDAT0 & 0x3ff; } </syntaxhighlight> 修改了这两个函数,原来的adc_test函数里调用的adc读取函数也要对应进行修改。 参考这个adc_test,编写photoresistor_test函数。 需要修改的内容并不多,首先是修改adc_read参数,将通道0改为通道1。 然后我们想同时再把AIN0上的滑动电阻对应的电压也读出来,因此再做一次ADC0读取的操作。 <syntaxhighlight lang="c" > void photoresistor_test(void) { int val, val0; double vol, vol0; int m, m0; /* 整数部分 */ int n, n0; /* 小数部分 */ //adc_init(); while (1) { val = adc_read(1); vol = (double)val/1023*3.3; /* 1023----3.3v */ m = (int)vol; /* 3.01, m = 3 */ vol = vol - m; /* 小数部分: 0.01 */ n = vol * 1000; /* 10 */ val0 = adc_read(0); vol0 = (double)val0/1023*3.3; /* 1023----3.3v */ m0 = (int)vol0; /* 3.01, m = 3 */ vol0 = vol0 - m0; /* 小数部分: 0.01 */ n0 = vol0 * 1000; /* 10 */ /* 在串口上打印 */ printf("photoresistor vol: %d.%03dv, compare to threshold %d.%03dv\r", m, n, m0, n0); /* 3.010v */ /* 在LCD上打印 */ //fb_print_string(); } </syntaxhighlight> 以上就完成了我们的第一个目标。 现在开始做第二个目标,注册中断放在interrupt.c里实现。 硬件上RES_DO是EINT15。我们可以仿照之前的按键中断来编写本次的中断。 先分析一下中断,如图: [[File:chapter22_lesson1_003.png|800px]] GPG7作为中断引脚,它会先经过外部中断EINTMASK寄存器,才能进入到中断控制器。 所以,需要做以下操作: *①首先初始化: :: a.GPG7配置为中断引脚; :: b.设置中端触发方式:双边沿触发; :: c.设置EINTMASK使能中断; *②中断处理: :: a.分辨:读EINTPEND; :: b.读GPG7; 在原来的key_eint_init函数里配置GPG7为中断引脚: <syntaxhighlight lang="c" > /* 配置GPG7为中断引脚, 用于光敏电阻 */ GPGCON &= ~((3<<14)); GPGCON |= ((2<<14)); </syntaxhighlight> 然后设置中端触发方式:双边沿触发 <syntaxhighlight lang="c" > /* 设置中断触发方式: 双边沿触发 */ EXTINT0 |= (7<<0) | (7<<8); /* S2,S3 */ EXTINT1 |= (7<<12); /* S4 */ EXTINT2 |= (7<<12); /* S5 */ </syntaxhighlight> 最后再使能中断: <syntaxhighlight lang="c" > /* 使能中断GPG7/EINT15, 用于光敏电阻 */ EINTMASK &= ~((1<<15)); </syntaxhighlight> 修改key_eint_irq中断处理函数,先判断是哪个中断产生,再读取电平,打印。 <syntaxhighlight lang="c" > else if (val & (1<<15)) /* eint15, 用于光敏电阻 */ { if (val2 & (1<<7)) { printf("\n\rphotoresistor dark!\n\r"); } else { printf("\n\rphotoresistor light!\n\r"); } } </syntaxhighlight> 至此,代码就写完了,最后还要修改对应的Makefile和主函数。 =第002节_高精度延时函数= 在后续我们对讲解多个传感器,这几个传感器对时序的要求都比较高,比如温湿度传感器DH11,查看芯片手册时序,至少就需要微秒级的延时函数。 延时函数的方式一般有两种: *①:使用for循环,利用示波器等工具测得精确值; *②:使用定时器,通过不断检测定时器的计数值获得精确时间; 使用for循环的方式,可能会因为硬件的差异,导致延时函数不准,因此这里我们使用定时器的方式。 打开之前的timers.c文件,修改timer_init函数的配置。 PCLK仍然等于50000000,将prescaler value改为4,divider value设置为2, 这样,每减1, 对应0.2us;每减5, 对应1us;从50000减到0,对应10ms。 修改对应的寄存器: <syntaxhighlight lang="c" > TCFG0 = 4; /* Prescaler 0 = 4, 用于timer0,1 */ TCFG1 &= ~0xf; /* MUX0 : 1/2 */ /* 设置TIMER0的初值 */ TCNTB0 = 50000; /* 10Ms中断一次 */ </syntaxhighlight> 我们先写一个us延时的函数,然后ms延时就调用us即可。 因此,us延时函数里,尽量少调用函数。 假如现在要延时nus,我们先将n*5,得到nus对应的“计数时钟数”。 然后如果传入“计数时钟周期”如果大于0,则一直计算过去了多少个“计数时钟数”,与传入的“计数时钟数”相减,直到为零,退出循环,也就实现了延时nus。 怎样计算过去了多少个“计算周期”呢? 自然是当前的值,减去一开始进入函数的值。 但还有一种情况是定时器里的计数记到0时,会自动变成5000,计数计数,这时候,计算方式就变成了pre+(5000-cur): [[File:chapter22_lesson2_001.png|700px]] <syntaxhighlight lang="c" > /* 尽量少调用函数 */ void udelay(int n) { int cnt = n * 5; /* u us 对应n*5个计数值 */ int pre = TCNTO0; int cur; int delta; while (cnt > 0) { cur = TCNTO0; if (cur <= pre) delta = pre - cur; else delta = pre + (50000 - cur); cnt = cnt - delta; pre = cur; } } </syntaxhighlight> 然后时ms延时函数: <syntaxhighlight lang="c" > void mdelay(int m) { udelay(m*1000); } </syntaxhighlight> 我们可以写一个测试函数,简单的测试下是否可用,测试函数隔1分钟进行打印一下。 如果us不准的话,放大至s,会有比较大的偏差,这样可以进行粗略的检测,精确检测可以使用示波器等工具。 <syntaxhighlight lang="c" > void hrtimer_test(void) { int cnt = 0; while (1) { printf("delay one min: "); mdelay(60000); /* 延时1分钟 */ printf("%d\n\r", ++cnt); } } </syntaxhighlight> 前面延时里的计算还是比较耗费时间的,因此,我们尽量提高CPU的运行时钟,并且 将尽可能的启动icache、dcache和mmu。 此外,如果延时过程中,发生了中断,如果中断比较耗时的话,就会导致延时可能出现不准确,所以,我们可以延时之前关中断, 延时之后开中断; 课后作业: * a. 禁止icache, 禁止mmu, 修改lds, 测试延时函数是否还准确; * b. 测试延时之前关中断, 延时之后开中断; =第003节_DHT11温湿度传感器的使用= =第004节_DS18B20温度传感器介绍= =第005节_DS18B20温度传感器编程= =第006节_红外线遥控协议简介及编程思路= =第007节_前期编程_系统时间与环型缓冲区= =第008节_HS0038红外线接收器的编程_打印原始脉冲= =第009节_HS0038红外线接收器的编程_解析数据= ='''《《所有章节目录》》'''= <categorytree mode=all background*color:white;">ARM裸机加强版</categorytree> [[Category:ARM裸机加强版 ]] [[Category:Jz2440 ]] [[Category:Irda红外传感器]] [[Category:ds18b20温湿度传感器]] [[Category:光敏电阻传感器]]
返回至
第022课 传感器
。
导航
导航
WIKI首页
官方店铺
资料下载
交流社区
所有页面
所有产品
MPU-Linux开发板
MCU-单片机开发板
Linux开发系列视频
单片机开发系列视频
所有模块配件
Wiki工具
Wiki工具
特殊页面
页面工具
页面工具
用户页面工具
更多
链入页面
相关更改
页面信息
页面日志