今天碰到一个findViewById查找自定义view时返回NULL的问题,经过排查发现是构造函数调用错误:
public QiuQianAnimationView(Context context, AttributeSet attrs) {
super(context);
}
QiuQianAnimationView这个之定义view中,调用父类的构造函数没有传入AttributeSet
改为:
public QiuQianAnimationView(Context context, AttributeSet attrs) {
super(context,attrs);
}
即可
.NET Micro Framework 和.NET Compact Framework不同,并不是.NET Framework的子集,而是更贴近硬件底层,它提供了许多诸如GPIO、PWM、SPI、I2C和OneWire等硬件操作类库。由此可以让普通的软件开发人员相对平滑的过度到硬件开发领域,也足以让以前相对封闭的硬件系统通过二次开发的方式扩展硬件模块成为一种流行(随着物联网技术的深入发展,未来软件不仅需要组态化,硬件其实更需要组态化)。
由于非硬件研发出身,最早接触I2C接口是在2007年初次接触.NET Micro Framework的时候,当时并没有和实际的硬件打交道,而是从软件层面封装了一个可以进行I2C总线虚拟通信的模拟器(参见博文《.Net MicroFramework研究—带I2C总线的模拟器》),后来在2008年为TI DM355的芯片移植.NET Micro Framework,我的任务就是I2C、USB等硬件驱动的开发。在TI DM355上有一个红外遥控接收器单元(MSP430单片)和DM355通过I2C接口进行通信,从而让.NET Micro Framework可以接收遥控器的按键信息。
后续在Cortex-M3平台,所接触的模块都是SPI接口的,I2C接口的反而没有,所以相关接口一直没有调试。最近有一个客户在使用物联网智能网关时候,发现引出的IO不够,需要扩展几路AD,推荐了一个基于PCF8591芯片的AD/DA转换模块,所以我才得以再次深入研究I2C接口。
如下是.NET Micro Framework的I2C接口类库:
public class I2CDevice : IDisposable
{
public I2CDevice.ConfigurationConfig;
protectedbool m_disposed;
publicI2CDevice(I2CDevice.Configurationconfig);
public static I2CDevice.I2CReadTransaction CreateReadTransaction(byte[] buffer);
public static I2CDevice.I2CWriteTransaction CreateWriteTransaction(byte[] buffer);
public void Dispose();
public int Execute(I2CDevice.I2CTransaction[] xActions, int timeout);
public class Configuration
{
publicreadonly ushortAddress;
publicreadonly intClockRateKhz;
publicConfiguration(ushort address, int clockRateKhz);
}
public sealed class I2CReadTransaction : I2CDevice.I2CTransaction{}
public class I2CTransaction
{
publicreadonly byte[]Buffer;
protectedI2CTransaction(byte[] buffer);
}
public sealed class I2CWriteTransaction : I2CDevice.I2CTransaction{}
}
I2C的读写操作通过定义I2CDevice.I2CTransaction数组,可以实现批量操作。
物联网智能网关基于STM32F207/STM32F407芯片,其I2C接口即支持主模式也支持从模式(I2C接口类仅支持主模式),总线速度支持两种,标准速度(高达100KHz)和快速(400KHz),I2C地址支持7位和10位两种(驱动只支持7位地址)。
PCF8591芯片是8位A/D和D/A转换器,4路模拟输入,1路模拟输出。其I2C地址是可以进行硬件编码的(3个地址引脚A0、A1和A2),其地址编码规则如下:
固定部分
编码部分
1
0
0
1
A2
A1
A0
R/W
最高位 最低位
对I2C来说,一般读操作的时候地址的最低位为1,写操作的时候地址的最低位为0。我们选用的模块,看原理图可知,A2、A1和A0管脚都被直接连接到GND,所以这部分都是0,所以对读地址来说是0x91,写地址是0x90。.NET Micro Framework底层I2C驱动实现的时候,地址会自动左移,最低位是不算地址的一部分的(所以才说是7位地址支持),所以我们在填写I2C模块地址的时候,要填写的是读地址(或写地址)右移一位的数字,也就是0x48。
总线频率我们理论上可以选择10K到400K,这里我们选择100K。
下面我们详细介绍一下PCF8591是如何进行AD读取和DA输出的。
读AD操作:
写操作
读操作
地址
控制字
地址
AD数据
写DA操作
写操作
地址
控制字
DA数据
控制字的定义如下:
0
X
X
X
0
X
X
X
DA标志
模拟输入模式
自动增量
通道号:0~3
对AD0~AD3通道来说,我们常用的控制字的值为0x0,0x1,0x2,0x3。
对DA来说,我们常用的控制字的值为0x40。
注意:如果我们循环读取AD0至AD3,由于读周期读取的AD转换值,其实是上一次的转换结果,所以我们读取AD1的时候,其实是读取的AD0,依次类推(上电第一次读取的值是0x80)。
有了以上知识,我们就可以进行编程了,核心代码如下:
public static void Main()
{
I2CDeviceI2CBus = new I2CDevice(new I2CDevice.Configuration(0x48, 100));
byte[]bytAD = new byte[4];
bytebytDA = 0;
while(true)
{
for(byte i = 0; i < 4; i++)
{
byte[]bytWData = new byte[1]{ i };
byte[]bytRData = new byte[1];
I2CDevice.I2CTransaction[] i2c = newI2CDevice.I2CTransaction[2];
i2c[0] = I2CDevice.CreateWriteTransaction(bytWData);
i2c[1] = I2CDevice.CreateReadTransaction(bytRData);
I2CBus.Execute(i2c, 100);
bytAD[i - 1 < 0 ? 3 : i- 1] = bytRData[0];
}
Debug.Print("AD0=" + ShowData(bytAD[0]) + " AD1=" + ShowData(bytAD[1]) + " AD2=" + ShowData(bytAD[2]) + " AD3=" + ShowData(bytAD[3]));
byte[]bytWData1 = new byte[2]{ 0x40, bytDA };
I2CDevice.I2CTransaction[] i2c1 = newI2CDevice.I2CTransaction[1];
i2c1[0] = I2CDevice.CreateWriteTransaction(bytWData1);
I2CBus.Execute(i2c1, 100);
Debug.Print("DA0=" + ShowData(bytDA));
bytDA += 10;
Thread.Sleep(1000);
}
}
PCF8591模块和物联网智能网关接线有四根,分别是VCC、GND、SCL和SDA,VCC可以接5V或3.3V(我们接3.3V)、SCL接PB6,SDA接PB7。程序部署运行的效果图如下:
通过旋转模块的上的模拟开关,我们会发现AD3的值可以由0向255变化。
注意:为了正常运行本实例,物联网智能网关固件版本需要升级到V1.7.15以上,TinyBooter也需要同步升级。
固件下载地址:http://www.sky-walker.com.cn/MFRelease/firmware/MFv42_YF_Wisteria207.rar
----------------------------------------------------
源码下载:http://www.sky-walker.com.cn/MFRelease/Sample/PCF859IT_I2C.rar
MF简介:http://blog.csdn.net/yefanqiu/article/details/5711770
MF资料:http://www.sky-walker.com.cn/News.asp?Id=25
学习新知识我的一个方法是:
1.先把握住新东西大的一个框架、涉及的内容、范围;
简而言之:理清架构,把握重点;
2.在看清楚大的框架所涉及的知识时,再度量哪些是我所需要必须学习的(重点),哪些是现在不急着掌握的可以以后学习;
简而言之:去我所需,为我所用;
3.理论归理论,掌握理论知识的基础上,加以应用,能更深刻的理解这些知识点;
简而言之:实践出真知;
声明:本文部分参考自--《STM32自学笔记集合》
第一讲:
STM32是Cortex-M3系列之一,而Cortex-M3处理器采用的是ARMv7-M架构。
系统架构:
希望大家关注AHB总线,这是贯穿所有外设的一条总线,上图可知:AHB经过桥接,由APB1、APB2控制着几乎所有外设;
希望大家关注AHB总线,这是贯穿所有外设的一条总线,上图可知:AHB经过桥接,由APB1、APB2控制着几乎所有外设;
&& APB2属于高速设备; (控制着如:ADC、GPIO、EXIT、TIM1等外设)
&& APB1属于低速设备; (控制着如:DAC、TIMx、USART、I2C等外设)
固件库函数:
STM32有两种软件开发方式:用库和不用库;
我们开讲一下用库开发:
固件库函数:该函数库是一个固件函数包,它由程序、数据结构和宏组成,包括了为控制器所有的外设性能特征。该函数还包括每个外设驱动描述和应用实例。
通过使用固件函数库,无需深入掌握细节,用户也可以轻松包括每一个外设。因此,固件库大大减少了用户编程的时间,降低开发成本;
每个外设驱动都由一组函数组成,这组函数覆盖了该外设所有功能,每个器件的开发都由一个通用的API(application programming interface应用编程接口)驱动,API对该驱动程序的结构,函数和参数名称都进行了标准化;
用库函数开发的优点: 1. 固件库大大减少了用户编程的时间,降低开发成本;
2.对初学者容易上手,无需深入掌握细节,也可以轻松包括每一个外设。
缺点:(1)没有深入系统架构,寄存器的控制的掌握和理解;
(2)编译效率低,速度慢;
不用库开发的优缺点:反之;
函数库一般放在FWlib目录下的inc文件夹内:如图
用库入门,用寄存器提高:
下面是库文件的结构图:
根据这张图课家里自己的工程;
应用层软件是依赖于第二层的定义和第三层相关库函数文件完成的;如果你不想用库,你可以利用stm32f10x.h中寄存器和结构体的定义直接对寄存器操作。而用库开发则是依靠第三层已经定义好的一些函数直接进行函数调用。下面将会解释几个关键头文件。
stmf10x.h : 定义了芯片类型及外设,并引入了另外三个头文件
# include “core_cm3.h” //arm公司为了标准化M3内核的一些声明
# include “system_stm32f10x.h” //系统时钟APB1、APB2时钟的一些外部声明
# include <stdint.h> //编译器及内部参量类型定义
上图中stm32f10x_it.c是专门存放中断函数的。
在引入头文件过后,定义了一些变量类型,和外设相关的结构体、外设常量。
做好这些文件衔接的工程中还需要一个启动文件,虽然说开发时不需要你去了解,因为早已有人先写好了,但是希望有兴趣的可以去了解启动过程下是如何运作的。
一下按照对软件结构的理解,建立的一套工程模板。原因是如果使用库,那么建立工程时会很繁琐,如果有了一套适宜自己的模板,那么开发起来会很顺手。
另外需要知道的缩略词,如图:
希望自己理解建立一个工程模板;这样才是有所用,否则看这篇文章也是浪费时间;