AM2321采用的是单总线控制。
AM2321单总线介绍
查看am2321的数据手册
单总线通信特殊说明
- 典型应用电路中建议连接线长度短于 30 米时用 5.1K 上拉电阻,大于 30 米时根据实际情况降低上拉电阻的阻值。
- 使用 3.3V 电压供电时连接线长度不得大于 30cm。否则线路压降会导致传感器供电不足,造成测量偏差。
- 读取传感器最小间隔时间为 2S;读取间隔时间小于 2S,可能导致温湿度不准或通信不成功等情况。
- 每次读出的温湿度数值是上一次测量的结果,欲获取实时数据,需连续读取两次,建议连续多次读取传感器, 且每次读取传感器间隔大于 2 秒即可获得准确的数据。
单总线说明
AM2321 器件采用简化的单总线通信。单总线即只有一根数据线,系统中的数据交换、控制均由数据线完成。设备(微处理器)通过一个漏极开路或三态端口连至该数据线,以允许设备在不发送数据时能够释放总线,而让其它设备使用总线;单总线通常要求外接一个约 5.1kΩ的上拉电阻,这样,当总线闲置时,其状态为高电平。由于它们是主从结构,只有主机呼叫传感器时,传感器才会应答,因此主机访问传感器都必须严格遵循单总线序列,如果出现序列混乱,传感器将不响应主机。
单总线传送数据定义
SDA 用于微处理器与 AM2321 之间的通讯和同步,采用单总线数据格式,一次传送 40 位数据,高位先出。具体通信时序如下图1所示,通信格式 如下表1单总线格式所示:
图1 通信时序
表1 单总线格式
单总线数据计算示例
示例一:接收到的 40 位数据为:
[tr]0000 00101001 00100000 00010000 11011010 0010[/tr]
湿度高 8 位 计算: | 湿度低 8 位 | 温度高 8 位 | 温度低 8 位 | 校验位 |
0000 0010+1001 0010 +0000 0001+0000 1101= 1010 0010(校验位)接收数据正确:
湿度: 0000 0010 1001 0010 = 0292H (十六进制)= 2× 256 + 9× 16 + 2 = 658=> 湿度 = 65.8%RH温度: 0000 0001 0000 1101 = 10DH(十六进制) = 1× 256 + 0× 16 + 13 = 269=> 温度= 26.9℃
单总线时序说明
主机从 AM2321 读取的温湿度数据总是前一次的测量值,如两次测量间隔时间很长,请连续读两次以第二次获得的值为实时温湿度值,同时两次读取间隔时间最小为 2S。
读写的流程图
程序代码编写
添加设备树
在设备树
arch/arm64/boot/dts/rockchip/rk3399pro-toybrick-prop-linux.dts
中添加
gpio-am2321{
status = "okay";
compatible = "gpio-am2321";
gpio-am2321 = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>;
};
驱动编写
匹配设备节点
static const struct of_device_id of_gec_am2321_match[] = {
{ .compatible = "gpio-am2321", .data = NULL}, //compatible 兼容属性名,需与设备树节点的属性一致
{ /* NULL */},
};
static struct platform_driver gec3399_am2321_driver =
{
.probe = rk_am_probe,
.remove = rk_am_remove,
.driver = {
.name ="gpio-am2321",
.of_match_table = of_gec_am2321_match, //设备树设备匹配
}
};
文件探索
static int rk_am_probe(struct platform_device *pdev)
{
int ret;
struct device_node *buz_node = pdev->dev.of_node;
printk(KERN_INFO "%sn", __func__);
printk(KERN_INFO"gpio-am2321 math succeen");
ret = misc_register(&gec3399_am_misc); //注册字符设备
if(ret < 0){
printk(KERN_INFO"misc register errorn");
goto err_misc_register;
}
gpio_am = of_get_named_gpio(buz_node,"gpio-am2321", 0); //从设备树获取GPIO号
if (!gpio_is_valid(gpio_am)) {
printk("gpio-am2321: %d is invalidn",gpio_am);
ret = -ENODEV;
goto err_get_gpio;
}
printk(KERN_INFO"gpio-am2321 = %dn",gpio_am);
gpio_free(gpio_am);
ret = gpio_request(gpio_am,"ak8963c_DYDR"); //申请gpio_am引脚为GPIO模式
if(ret < 0){
printk("gpio_request gpio = ak8963c_DYDR errorn");
goto err_get_gpio;
}
ret = gpio_direction_output(gpio_am,0); //初始划蜂鸣器为关闭状态
if(ret < 0){
printk("gpio direction input gpio = ak8963c_DYDR errorn");
goto err_gpio_direction;
}
printk( KERN_ALERT "am2321 dirve install succeen");
return 0;
err_gpio_direction:
gpio_free(gpio_am);
err_get_gpio:
misc_deregister(&gec3399_am_misc);
err_misc_register:
return ret;
}
杂项设备
static struct miscdevice gec3399_am_misc = {
.minor = MISC_DYNAMIC_MINOR,
.fops = &gec3399_am_fops,
.name = "am2321_drv",
}; //混杂设备结构体定义和初始化
文件操作集
static const struct file_operations gec3399_am_fops = {
.owner = THIS_MODULE,
.read = am2321_read,
.release = gec3399_am_release,
}; //文件操作集结构体
读温湿度接口
static ssize_t am2321_read (struct file *file, char __user *buffer, size_t size, loff_t *offset)
{
int ret;
unsigned long flags;
unsigned short verify_sum = 0;
//int i;
debug("<0>""enter am2321_read functionnn");
debug("<0>""am2321_PIN level = %dn",read_pin_level());
/* 因为am2321的时序要求很高,所以在读温湿度的时候要让代码进入临界区,防止内核调度和抢占 */
local_irq_save(flags);
ret = am2321_start_signal();
if(ret ==1) //如果收到应答
am2321_read_data();
else
return -1;
local_irq_restore(flags);
debug("<0>""%x %x %x %x %xn",data_buf[0],data_buf[1],data_buf[2],data_buf[3],data_buf[4]);
verify_sum = data_buf[0]+data_buf[1]+data_buf[2]+data_buf[3];
if(verify_sum == data_buf[4])
{
/* 将读取的温湿度数据拷贝到用户空间 */
ret = copy_to_user(buffer, data_buf, 4);
if(ret < 0)
{
debug("<0>""copy to user errn");
return -EAGAIN;
}
}
else{
printk("data is errorrn");
}
return 0;
}
开始信号
static bool am2321_start_signal(void)
{
int i = 0;
set_pin_level(1);
mdelay(50);
//第一步:拉低10ms
set_pin_level(0);
mdelay(1);
//第二步:拉高30us
set_pin_level(1);
udelay(30);
//第三步:等待am2321主动拉低
while(read_pin_level()==1)
{
i++;
udelay(1);
if(i>500){
debug("<0>""wait pin for low level out time! %s %dn",__FUNCTION__,__LINE__);
goto err0;
}
}
//debug("<0>""(1)wait pin for low level success! i=%dn",i);
i = 0;
//第四步:等待am2321拉高,不超过85us
while(read_pin_level()==0)
{
i++;
udelay(1);
if(i>90){
debug("<0>""wait pin for hight level out time! %s %dn",__FUNCTION__,__LINE__);
goto err0;
}
}
//debug("<0>""(2)wait pin for hight level success! i=%dn",i);
i = 0;
//第五步:等待am2321拉低,不超过85us
while(read_pin_level()==1)
{
i++;
udelay(1);
if(i>500){
debug("<0>""wait pin for data input out time! %s %dn",__FUNCTION__,__LINE__);
goto err0;
}
}
//debug("<0>""(3)wait pin for data input success! i=%dn",i);
return 1;
err0:
set_pin_level(1);
return 0;
}
温湿度数据读取一个byte字节
static bool am2321_read_byte(char *byte)
{
int time=0;
int i = 0;
for(i=0; i<8; i++)
{
*byte = *byte << 1;
//udelay(30);
time=0;
//等待高电平,开始读取数据
while(read_pin_level()==0)
{
time++;
udelay(1);
if(time > 500)
{
debug("<0>""wait pin for hight level out time! %s %dn",__FUNCTION__,__LINE__);
return 0;
}
}
//如果超过了30us后还是高电平,就是逻辑高电平,稳妥采取55us后读取。
udelay(40);
*byte |= read_pin_level();
//等待一次数据读取结束
time = 0;
while(read_pin_level()==1)
{
time++;
udelay(1);
if(time > 500)
{
debug("<0>""wait pin for low level out time! %s %dn",__FUNCTION__,__LINE__);
return 0;
}
}
}
return 1;
}
上层测试代码
#include
#include
#include
#include
#include
/* 程序的入口函数 */
int main(int argc, char *argv[])
{
int fd;
char buf[4]; /* 定义存放数据的数组 */
int length;
float tem,hum;
/* 以只读方式打开设备节点 */
fd = open("/dev/am2321_drv", O_RDONLY);
if(fd < 0)
{
printf("open failed!n");
return -1;
}
while(1)
{
sleep(3);
length = read(fd, buf, 4); /* 读取温湿度数据 */
/* 将数据从终端打印出来
* buf[0] 湿度高8位部分
* buf[1] 湿度低8位部分
* buf[2] 温度高8位部分
* buf[3] 温度低8位部分
*/
hum = (float)((buf[0]<<8)|buf[1])/10;
tem = (float)((buf[2]<<8)|buf[3])/10;
printf("Temp: %xH ==> %.1f Humi: %xH ==> %.1fn", (buf[2]<<8)|buf[3],tem,(buf[0]<<8)|buf[1],hum);
}
/* 关闭DHT11设备节点 */
close(fd);
return 0;
}
Makefile编写
obj-m += am2321.o
KERNELDIR:=/file/RK3399Pro/rk3399pro_git_repo/kernel
PWD:=$(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
test:
aarch64-linux-gnu-gcc am2321_test.c -o am2321_test
clean:
rm -rf *.o *.order .*.cmd *.ko *.mod.c *.symvers *.tmp_versions
测试步骤
编译源码
在ubuntu中输入:
make
得到驱动目标文件am2321.ko
输入:
make test
得到测试目标文件:am2321_test
加载驱动
在开发板命令终端输入:
insmod am2321.ko
执行测试程序
在开发板命令终端输入:
chmod 777 am2321_test
./am2321_test
实验现象
读取温湿度数据
root@linaro-alip:~# ./test/am2321_test
[ 686.323038] <0>enter am2321_read functionn
[ 686.323482] <0>am2321_PIN level = 1
[ 686.379139] <0>1 b5 1 19 d0
Temp: 119H ==> 28.1 Humi: 1b5H ==> 43.7
AM2321采用的是单总线控制。
AM2321单总线介绍
查看am2321的数据手册
单总线通信特殊说明
- 典型应用电路中建议连接线长度短于 30 米时用 5.1K 上拉电阻,大于 30 米时根据实际情况降低上拉电阻的阻值。
- 使用 3.3V 电压供电时连接线长度不得大于 30cm。否则线路压降会导致传感器供电不足,造成测量偏差。
- 读取传感器最小间隔时间为 2S;读取间隔时间小于 2S,可能导致温湿度不准或通信不成功等情况。
- 每次读出的温湿度数值是上一次测量的结果,欲获取实时数据,需连续读取两次,建议连续多次读取传感器, 且每次读取传感器间隔大于 2 秒即可获得准确的数据。
单总线说明
AM2321 器件采用简化的单总线通信。单总线即只有一根数据线,系统中的数据交换、控制均由数据线完成。设备(微处理器)通过一个漏极开路或三态端口连至该数据线,以允许设备在不发送数据时能够释放总线,而让其它设备使用总线;单总线通常要求外接一个约 5.1kΩ的上拉电阻,这样,当总线闲置时,其状态为高电平。由于它们是主从结构,只有主机呼叫传感器时,传感器才会应答,因此主机访问传感器都必须严格遵循单总线序列,如果出现序列混乱,传感器将不响应主机。
单总线传送数据定义
SDA 用于微处理器与 AM2321 之间的通讯和同步,采用单总线数据格式,一次传送 40 位数据,高位先出。具体通信时序如下图1所示,通信格式 如下表1单总线格式所示:
图1 通信时序
表1 单总线格式
单总线数据计算示例
示例一:接收到的 40 位数据为:
[tr]0000 00101001 00100000 00010000 11011010 0010[/tr]
湿度高 8 位 计算: | 湿度低 8 位 | 温度高 8 位 | 温度低 8 位 | 校验位 |
0000 0010+1001 0010 +0000 0001+0000 1101= 1010 0010(校验位)接收数据正确:
湿度: 0000 0010 1001 0010 = 0292H (十六进制)= 2× 256 + 9× 16 + 2 = 658=> 湿度 = 65.8%RH温度: 0000 0001 0000 1101 = 10DH(十六进制) = 1× 256 + 0× 16 + 13 = 269=> 温度= 26.9℃
单总线时序说明
主机从 AM2321 读取的温湿度数据总是前一次的测量值,如两次测量间隔时间很长,请连续读两次以第二次获得的值为实时温湿度值,同时两次读取间隔时间最小为 2S。
读写的流程图
程序代码编写
添加设备树
在设备树
arch/arm64/boot/dts/rockchip/rk3399pro-toybrick-prop-linux.dts
中添加
gpio-am2321{
status = "okay";
compatible = "gpio-am2321";
gpio-am2321 = <&gpio1 RK_PB2 GPIO_ACTIVE_LOW>;
};
驱动编写
匹配设备节点
static const struct of_device_id of_gec_am2321_match[] = {
{ .compatible = "gpio-am2321", .data = NULL}, //compatible 兼容属性名,需与设备树节点的属性一致
{ /* NULL */},
};
static struct platform_driver gec3399_am2321_driver =
{
.probe = rk_am_probe,
.remove = rk_am_remove,
.driver = {
.name ="gpio-am2321",
.of_match_table = of_gec_am2321_match, //设备树设备匹配
}
};
文件探索
static int rk_am_probe(struct platform_device *pdev)
{
int ret;
struct device_node *buz_node = pdev->dev.of_node;
printk(KERN_INFO "%sn", __func__);
printk(KERN_INFO"gpio-am2321 math succeen");
ret = misc_register(&gec3399_am_misc); //注册字符设备
if(ret < 0){
printk(KERN_INFO"misc register errorn");
goto err_misc_register;
}
gpio_am = of_get_named_gpio(buz_node,"gpio-am2321", 0); //从设备树获取GPIO号
if (!gpio_is_valid(gpio_am)) {
printk("gpio-am2321: %d is invalidn",gpio_am);
ret = -ENODEV;
goto err_get_gpio;
}
printk(KERN_INFO"gpio-am2321 = %dn",gpio_am);
gpio_free(gpio_am);
ret = gpio_request(gpio_am,"ak8963c_DYDR"); //申请gpio_am引脚为GPIO模式
if(ret < 0){
printk("gpio_request gpio = ak8963c_DYDR errorn");
goto err_get_gpio;
}
ret = gpio_direction_output(gpio_am,0); //初始划蜂鸣器为关闭状态
if(ret < 0){
printk("gpio direction input gpio = ak8963c_DYDR errorn");
goto err_gpio_direction;
}
printk( KERN_ALERT "am2321 dirve install succeen");
return 0;
err_gpio_direction:
gpio_free(gpio_am);
err_get_gpio:
misc_deregister(&gec3399_am_misc);
err_misc_register:
return ret;
}
杂项设备
static struct miscdevice gec3399_am_misc = {
.minor = MISC_DYNAMIC_MINOR,
.fops = &gec3399_am_fops,
.name = "am2321_drv",
}; //混杂设备结构体定义和初始化
文件操作集
static const struct file_operations gec3399_am_fops = {
.owner = THIS_MODULE,
.read = am2321_read,
.release = gec3399_am_release,
}; //文件操作集结构体
读温湿度接口
static ssize_t am2321_read (struct file *file, char __user *buffer, size_t size, loff_t *offset)
{
int ret;
unsigned long flags;
unsigned short verify_sum = 0;
//int i;
debug("<0>""enter am2321_read functionnn");
debug("<0>""am2321_PIN level = %dn",read_pin_level());
/* 因为am2321的时序要求很高,所以在读温湿度的时候要让代码进入临界区,防止内核调度和抢占 */
local_irq_save(flags);
ret = am2321_start_signal();
if(ret ==1) //如果收到应答
am2321_read_data();
else
return -1;
local_irq_restore(flags);
debug("<0>""%x %x %x %x %xn",data_buf[0],data_buf[1],data_buf[2],data_buf[3],data_buf[4]);
verify_sum = data_buf[0]+data_buf[1]+data_buf[2]+data_buf[3];
if(verify_sum == data_buf[4])
{
/* 将读取的温湿度数据拷贝到用户空间 */
ret = copy_to_user(buffer, data_buf, 4);
if(ret < 0)
{
debug("<0>""copy to user errn");
return -EAGAIN;
}
}
else{
printk("data is errorrn");
}
return 0;
}
开始信号
static bool am2321_start_signal(void)
{
int i = 0;
set_pin_level(1);
mdelay(50);
//第一步:拉低10ms
set_pin_level(0);
mdelay(1);
//第二步:拉高30us
set_pin_level(1);
udelay(30);
//第三步:等待am2321主动拉低
while(read_pin_level()==1)
{
i++;
udelay(1);
if(i>500){
debug("<0>""wait pin for low level out time! %s %dn",__FUNCTION__,__LINE__);
goto err0;
}
}
//debug("<0>""(1)wait pin for low level success! i=%dn",i);
i = 0;
//第四步:等待am2321拉高,不超过85us
while(read_pin_level()==0)
{
i++;
udelay(1);
if(i>90){
debug("<0>""wait pin for hight level out time! %s %dn",__FUNCTION__,__LINE__);
goto err0;
}
}
//debug("<0>""(2)wait pin for hight level success! i=%dn",i);
i = 0;
//第五步:等待am2321拉低,不超过85us
while(read_pin_level()==1)
{
i++;
udelay(1);
if(i>500){
debug("<0>""wait pin for data input out time! %s %dn",__FUNCTION__,__LINE__);
goto err0;
}
}
//debug("<0>""(3)wait pin for data input success! i=%dn",i);
return 1;
err0:
set_pin_level(1);
return 0;
}
温湿度数据读取一个byte字节
static bool am2321_read_byte(char *byte)
{
int time=0;
int i = 0;
for(i=0; i<8; i++)
{
*byte = *byte << 1;
//udelay(30);
time=0;
//等待高电平,开始读取数据
while(read_pin_level()==0)
{
time++;
udelay(1);
if(time > 500)
{
debug("<0>""wait pin for hight level out time! %s %dn",__FUNCTION__,__LINE__);
return 0;
}
}
//如果超过了30us后还是高电平,就是逻辑高电平,稳妥采取55us后读取。
udelay(40);
*byte |= read_pin_level();
//等待一次数据读取结束
time = 0;
while(read_pin_level()==1)
{
time++;
udelay(1);
if(time > 500)
{
debug("<0>""wait pin for low level out time! %s %dn",__FUNCTION__,__LINE__);
return 0;
}
}
}
return 1;
}
上层测试代码
#include
#include
#include
#include
#include
/* 程序的入口函数 */
int main(int argc, char *argv[])
{
int fd;
char buf[4]; /* 定义存放数据的数组 */
int length;
float tem,hum;
/* 以只读方式打开设备节点 */
fd = open("/dev/am2321_drv", O_RDONLY);
if(fd < 0)
{
printf("open failed!n");
return -1;
}
while(1)
{
sleep(3);
length = read(fd, buf, 4); /* 读取温湿度数据 */
/* 将数据从终端打印出来
* buf[0] 湿度高8位部分
* buf[1] 湿度低8位部分
* buf[2] 温度高8位部分
* buf[3] 温度低8位部分
*/
hum = (float)((buf[0]<<8)|buf[1])/10;
tem = (float)((buf[2]<<8)|buf[3])/10;
printf("Temp: %xH ==> %.1f Humi: %xH ==> %.1fn", (buf[2]<<8)|buf[3],tem,(buf[0]<<8)|buf[1],hum);
}
/* 关闭DHT11设备节点 */
close(fd);
return 0;
}
Makefile编写
obj-m += am2321.o
KERNELDIR:=/file/RK3399Pro/rk3399pro_git_repo/kernel
PWD:=$(shell pwd)
default:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
test:
aarch64-linux-gnu-gcc am2321_test.c -o am2321_test
clean:
rm -rf *.o *.order .*.cmd *.ko *.mod.c *.symvers *.tmp_versions
测试步骤
编译源码
在ubuntu中输入:
make
得到驱动目标文件am2321.ko
输入:
make test
得到测试目标文件:am2321_test
加载驱动
在开发板命令终端输入:
insmod am2321.ko
执行测试程序
在开发板命令终端输入:
chmod 777 am2321_test
./am2321_test
实验现象
读取温湿度数据
root@linaro-alip:~# ./test/am2321_test
[ 686.323038] <0>enter am2321_read functionn
[ 686.323482] <0>am2321_PIN level = 1
[ 686.379139] <0>1 b5 1 19 d0
Temp: 119H ==> 28.1 Humi: 1b5H ==> 43.7
举报