[嵌入式Linux] TINY6410环境监测系统,含嵌入式C驱动

实验开始之前,简单的归纳一些重点。单线总线高电平为闲置状态。

单片机访问DS18B20必须遵守,DS18B20 复位–>执行ROM 指令–>执行DS18B20 功能指令。而在单点上,可以直接跳过ROM 指令。DS18B20 的转换精度默认为12 位,而分辨率是0.0625。

DS18B20 温度读取函数参考步骤:

DS18B20 开始转换:

1.DS18B20 复位。

2.写入跳过ROM 的字节命令,0xCC。

3.写入开始转换的功能命令,0x44。

4.延迟大约750~900 毫秒

 

DS18B20 读暂存数据:

1.DS18B20 复位。

2.写入跳过ROM 的字节命令,0xCC。

3.写入读暂存的功能命令,0xee。

4.读入第0 个字节LS Byte,转换结果的低八位。

5.读入第1 个字节MS Byte,转换结果的高八位。

6.DS18B20 复位,表示读取暂存结束。

 

数据求出十进制:

1.整合LS Byte 和MS Byte 的数据

2.判断是否为正负数(可选)

3.求得十进制值。正数乘以0.0625,一位小数点乘以0.625,二位小数点乘以6.25。

4.十进制的“个位”求出。

 

3.S3C的一些说明

3.1 s3c_gpio_cfgpin等内核导出函数

  • s3c_gpio_cfgpin()

//设置相应gpio口的工作模式,是输入,输出还是其他的

//s3c2410_gpio_cfgpin(S3C2410_GPB(5),S3C2410_GPIO_INPUT),就是设置GPB5为输入模式

//如pin=S3C2410_GPB5 function=00 则:设置S3C2410_GPB5为输入口

//如pin=S3C2410_GPB5 function=01 则:设置S3C2410_GPB5为输出口

//如pin=S3C2410_GPB5 function=10 则:设置S3C2410_GPB5为多功能口

void s3c2410_gpio_cfgpin(unsigned int pin, unsigned int function)

  • s3c_gpio_getcfg()

//作用:返回对应的GPIO的配置情况,例如pin=s3c2410_GPB5,则返回S3C2410_GBP5的配置情况

unsigned int s3c2410_gpio_getcfg(unsigned int pin)

 

  • s3c_gpio_pullup()

//设置相应的GPIO口的上拉电阻,当to为1时候,相应的pin引脚设置为1,表示该GPIO要上拉电阻,to为0时候,相应的pin引脚配置为0,表示不要上拉电阻

//如 pin=S3C2410_GPB5  to=0 则:设置S3C2410_GPB5的不要上拉电阻

//如 pin=S3C2410_GPB5  to=1 则:设置S3C2410_GPB5的要上拉电阻

void s3c2410_gpio_pullup(unsigned int pin, unsigned int to)

 

  • s3c_gpio_setpin()

//当GPIO的工作为输入时候,设置某个GPIO口的值

//s3c2410_gpio_setpin(S3C2410_GPB(5),0),设置GPB5的输入值是0,就是低电平,to为1,表示该pin输出为1

//如pin=S3C2410_GPB5  to=0 则:设置S3C2410_GPB5的输出值为0

//如pin=S3C2410_GPB5  to=1 则:设置S3C2410_GPB5的输出值为1

void s3c2410_gpio_setpin(unsigned int pin, unsigned int to)

 

 

  • s3c_gpio_getpin()

//当GPIO为输出模式时候,获取GPIO的输出数值

//s3c2410_gpio_getpin(S3C2410_GPB(5)),获取GPB5的输出值

unsigned int s3c2410_gpio_getpin(unsigned int pin)

 

3.2 端口N控制寄存器

来自DS18B20文档。

端口N 控制寄存器包括三个控制寄存器,分别是GPNCON 、GPNDAT 、GPNPUD。

GPNCON 、GPNDAT 、GPNPUD 都是alive 部分。

寄存器 地址 读/写 描述 复位值
GPNCON 0x7F008830 读/写 端口N   配置寄存器 0x00
GPNDAT 0x7F008834 读/写 端口N   数据寄存器 未定义
GPNPUD 0x7F008838 读/写 端口N  上拉/下拉寄存器 0x55555555
GPNCON 描述 初始状态
GPN0 [1:0] 00=输入                01=输出

10=Ext.Interrupt[0]    11= Key pad ROW[0]

00
GPN1 [3:2] 00=输入                01=输出

10=Ext.Interrupt[1]    11= Key pad ROW[1]

00
GPN2 [5:4] 00=输入                01=输出

10=Ext.Interrupt[2]    11= Key pad ROW[2]

00
GPN3 [7:6] 00=输入                01=输出

10=Ext.Interrupt[3]    11= Key pad ROW[3]

00
GPN4 [9:8] 00=输入                01=输出

10=Ext.Interrupt[4]    11= Key pad ROW[4]

00
GPN5 [11:10] 00=输入                01=输出

10=Ext.Interrupt[5]    11= Key pad ROW[5]

00
GPN6 [13:12] 00=输入                01=输出

10=Ext.Interrupt[6]    11= Key pad ROW[6]

00
GPN7 [15:14] 00=输入                01=输出

10=Ext.Interrupt[7]    11= Key pad ROW[7]

00
GPN8 [17:16] 00=输入                01=输出

10=Ext.Interrupt[8]    11= Key pad ROW[8]

00
GPN9 [19:18] 00=输入                01=输出

10=Ext.Interrupt[9]    11= Key pad ROW[9]

00
GPN10 [21:20] 00=输入                 01=输出

10=Ext.Interrupt[10]    11= Key pad ROW[10]

00
GPN11 [23:22] 00=输入                 01=输出

10=Ext.Interrupt[11]    11= Key pad ROW[11]

00
GPN12 [25:24] 00=输入                 01=输出

10=Ext.Interrupt[12]    11= Key pad ROW[12]

00
GPN13 [27:26] 00=输入                01=输出

10=Ext.Interrupt[13]    11= Key pad ROW[13]

00
GPN14 [29:28] 00=输入                 01=输出

10=Ext.Interrupt[14]    11= Key pad ROW[14]

00
GPN15 [31:30] 00=输入                 01=输出

10=Ext.Interrupt[15]    11= Key pad ROW[15]

00

 

5.DS18B20驱动程序设计及测试

5.1 驱动程序


参考了一些网上的代码,并最终写了这份驱动并编译成功。

#include <linux/module.h> //模块编译必备头文件
#include <linux/fs.h> //file结构
#include <linux/kernel.h> //kernel:)
#include <linux/init.h> //init
#include <linux/delay.h> //udelay和mdelay取自这里
#include <linux/cdev.h> //cdev结构
#include <linux/device.h> //device
#include <linux/gpio.h> //gpio控制寄存器

#include <plat/gpio-cfg.h> //cfg等
#define DEVICE_NAME "ds18b20"
#define DQ S3C64XX_GPN(8)
#define DATA_IN S3C_GPIO_SFN(0)
#define DATA_OUT S3C_GPIO_SFN(1)
#define HIGH 1
#define LOW 0

static struct cdev cdev;
struct class *ds18b20_class;
static dev_t devno;
static int major = 232;
static unsigned char data[2];
/*复位
*/
void ds18b20_reset(void)
{
s3c_gpio_cfgpin(DQ, DATA_OUT); //设置GPN[8]引脚为输出模式
gpio_set_value(DQ, HIGH); //拉高总线
udelay(50);
gpio_set_value(DQ, LOW); //拉低总线
udelay(600); //并维持600us,要求480us~950us
gpio_set_value(DQ, HIGH); //拉高总线
udelay(150); //ds18b20回应,要求60us~240us
s3c_gpio_cfgpin(DQ, DATA_IN); //设置为输出模式,复位成功
}
/*写1个byte
*/
void ds18b20_wbyte(unsigned char data)
{
int i;
s3c_gpio_cfgpin(DQ, DATA_OUT); //配置GPN[8]为输出模式
for (i = 0; i < 8; ++i)
{
gpio_set_value(DQ, LOW);
udelay(1);
if (data & HIGH) //若data末位为1,则需要向总线上写'1'
{
gpio_set_value(DQ, HIGH); //电平设置为高,写'1'
}
else //若data末位为0,则需要向总线上写'0'
{
gpio_set_value(DQ,LOW); //电平设置为低,写'0'
}
udelay(60); //电平状态保持60us
gpio_set_value(DQ, HIGH);
udelay(15);
data >>= 1;
}
gpio_set_value(DQ, HIGH);
}
/*读单个byte
*/
unsigned char ds18b20_rbyte(void) 
{
int i;
unsigned char byte1 = 0;
for (i = 0; i < 8; ++i)
{
s3c_gpio_cfgpin(DQ, DATA_OUT); //设为输出模式
gpio_set_value(DQ, LOW); //拉低总线
udelay(5); //保持5us,要求是1us~15us
gpio_set_value(DQ, HIGH); //拉高总线
s3c_gpio_cfgpin(DQ, DATA_IN); //释放
byte1 >>= 1; //ret右移1位
if (gpio_get_value(DQ)) //获得DQ的值,把第7位置为‘1’
{
byte1 |= 0x80; 
}
udelay(60); //保持60us
}
s3c_gpio_cfgpin(DQ, DATA_OUT); //
return byte1;
}
/*读出LSB MSB
*/
void ds18b20_proc(void)
{
ds18b20_reset(); //复位
udelay(420); //保持
ds18b20_wbyte(0xcc); //写入0xcc,发跳过ROM命令
ds18b20_wbyte(0x44); //写入0x44,启动温度转换
mdelay(100); //等待温度转换结束
ds18b20_reset(); //复位
udelay(400);
ds18b20_wbyte(0xcc); 
ds18b20_wbyte(0xbe); //发读取温度命令
data[0] = ds18b20_rbyte(); //读温度值低8位,Byte0 LSB(50h)
data[1] = ds18b20_rbyte(); //读温度值高8位,Byte1 MSB(05h)

}

/*算出温度
*/
static ssize_t ds18b20_read(struct file *filp, char *buf, size_t len, loff_t *offset)
{
ds18b20_proc();
*buf = data[0] / 16 + data[1] * 16; //计算,不详说了,看说明
*(buf + 1) = (data[0] & 0x0f) * 10 / 16;// + (data[1] & 0x0f) * 100 / 16 % 10;
return 0;
}

/*
file 结构,不详说了
*/
static struct file_operations ds18b20_fops =
{
.owner = THIS_MODULE,
.read = ds18b20_read,
};
/*设备注册
*/
static int __init ds18b20_init(void)
{
int result;
devno = MKDEV(major, 0);
result = register_chrdev_region(devno, 1, DEVICE_NAME);
if (result)
{
printk("register failed\n"); 
return result;
}

cdev_init(&cdev, &ds18b20_fops);
cdev.owner = THIS_MODULE;
cdev.ops = &ds18b20_fops;

result = cdev_add(&cdev, devno, 1);

if (result)
{
printk("cdev add failed\n"); 
unregister_chrdev_region(devno, 1);
return result;
}

ds18b20_class = class_create(THIS_MODULE, "ds18b20_class");
if (IS_ERR(ds18b20_class))
{
printk("class create failed\n"); 
cdev_del(&cdev);
}

device_create(ds18b20_class, NULL, devno, DEVICE_NAME, DEVICE_NAME);
return 0;
}
/*设备卸载
*/
static void __exit ds18b20_exit(void)
{
device_destroy(ds18b20_class, devno);
class_destroy(ds18b20_class);
cdev_del(&cdev);
unregister_chrdev_region(devno, 1);
}
module_init(ds18b20_init);
module_exit(ds18b20_exit);
MODULE_LICENSE("GPL");

5.2 测试程序主要代码


void print_temperature()
{
int fd;
unsigned char buf[2];
fd=open("/dev/ds18b20",O_NDELAY);
if(fd <0 )
{
printf("Open Device DS18B20 failed.\r\n");
exit(1);
}
else
{
printf("Temperatrue as follows:\r\n");
while(1)
{
read(fd, buf, sizeof(buf));

printf("%d.%dC\r\n",buf[0], buf[1]);
sleep(1);

}
close(fd);
}
}

 

6.TINY6410和主机之间的TCP通信(传送温度)

6.1 客户端主要代码

注:LINUX主机上GCC编译;开发板上 ARM-LINUX-GCC编译,在linux上运行

客户端(TINY6410端运行,client)


#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>

#include "sys/ioctl.h"
#include "termios.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "sys/time.h"

int main()
{
int fd;
char buf[2];

int sockfd; //连接套接字
int len;
struct sockaddr_in address;
int result;
sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP); //设置

address.sin_family = AF_INET; //AF_INET,即INTERNET域
address.sin_addr.s_addr = inet_addr("192.168.1.1"); //主机IP
address.sin_port = 8080; //端口号

len = sizeof(address);

result = connect(sockfd,(struct sockaddr *)&address,len);


if(result == -1)
{
perror("oops:client1");
exit(1);
}
else
{
printf("Conversation started.\n");
}

fd=open("/dev/ds18b20",O_NDELAY); //打开设备
printf("Temperature as follows.(with the first one at 85.5C not available).\r\n");

while(1)
{
read(fd, buf, sizeof(buf));

write(sockfd,buf,sizeof(buf) + 1);
sleep(1);
}

close(fd);
close(sockfd);
exit(0);
}

6.2服务器(LINUX主机端,server)


#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
int server_sockfd,client_sockfd;//套接字,server_sockfd监听,client_socket连接
int server_len,client_len;
struct sockaddr_in server_address;
struct sockaddr_in client_address;

unlink("server_socket");
server_sockfd = socket(AF_INET,SOCK_STREAM,0); //设置套接字参数

server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = htonl(INADDR_ANY);
server_address.sin_port = 8080;


server_len = sizeof(server_address);
bind(server_sockfd,(struct sockaddr*)&server_address,server_len);
listen(server_sockfd,5); //开始监听
printf("waiting Temperature.\n");
while(1){
char ch;

client_len = sizeof(client_address);
client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address,&client_len);

unsigned char buf[2];
int i;
while(1){
read(client_sockfd,&buf,sizeof(buf)+1); //获取
printf(“"Temperature as follows.(with the first one at 85.5C not available).\n"”);
printf("Temperature is: %d.%d\n",buf[0],buf[1]); //输出温度
}
close(client_sockfd);
}
}

运行示例:

 

附:Linux TCP聊天程序

client.c客户端

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
int server_sockfd,client_sockfd;
int server_len,client_len;
struct sockaddr_in server_address;
struct sockaddr_in client_address;

unlink("server_socket");
server_sockfd = socket(AF_INET,SOCK_STREAM,0);

server_address.sin_family = AF_INET;
server_address.sin_addr.s_addr = inet_addr("192.168.1.101");
server_address.sin_port = 8080;


server_len = sizeof(server_address);
bind(server_sockfd,(struct sockaddr*)&server_address,server_len);
listen(server_sockfd,5);
printf("waiting A.\n");
while(1){
char ch;

client_len = sizeof(client_address);
client_sockfd = accept(server_sockfd,(struct sockaddr *)&client_address,&client_len);

char buf[500];
int i;
while(1){
read(client_sockfd,&buf,sizeof(buf)+1);

printf("B : %s\n",buf);

printf("A :");
scanf("%s",buf);

write(client_sockfd,buf,sizeof(buf) +1);
}
close(client_sockfd);
}
}

 

server.c服务器端

#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <unistd.h>
#include <stdlib.h>

int main()
{
int sockfd;
int len;
struct sockaddr_in address;
int result;
sockfd = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);

address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr("192.168.1.101");
address.sin_port = 8080;

len = sizeof(address);

result = connect(sockfd,(struct sockaddr *)&address,len);



if(result == -1)
{
perror("oops:client1");
exit(1);
}
else
{
printf("Conversation started.\n");
}

while(1){
char buf[500];

printf("B :");
scanf("%s",buf);

write(sockfd,buf,sizeof(buf) + 1);
read(sockfd,buf,sizeof(buf) + 1);
printf("A: %s\n",buf);
}
close(sockfd);
exit(0);
}

 

 

常见问题及处理:

  1. make menuconfig出现错误:
    *** Unable to find the ncurses libraries or the
    *** required header files.
    *** ‘make menuconfig’ requires the ncurses libraries.
    ***
    *** Install ncurses (ncurses-devel) and try again.
    ***
    make[1]: *** [scripts/kconfig/dochecklxdialog] 错误 1
    make: *** [menuconfig] 错误 2
    解决办法:
    sudo apt-get install libncurses5-dev
    成功!

 

2.然而当我make menuconfig时,在Device Drivers->Character device中找不到这个module。实际上,这个module是depends on CPU_S3C6410, 如果没有选择CPU_S3C6410,那么这个module就不会出现在选项里。解决方法,在make menuconfig里,System Type -> ARM System Type中选择SamSung_S3C64xx, 然后再到Character device,就有这个module了。。

分类上一篇:无,已是最新文章    分类下一篇:

Leave a Reply