项目概述:
雾霾,PM2.5就像汽车, 手机其他新生事物一样慢慢走入人们的生活之中,尤其是北方生活的人们。不过我们普通人只能买点口罩找点心里安慰,也许带的口罩也未必真的能够防住PM2.5。从技术人员的角度出发,自己就以实现PM2.5检测系统为主题申请参与米尔科技Ricoboard 开发板试用活动。当然也在Ricoboard开发板上测试了其他很多有用的功能,比如EEPROM,HDMI,RTC,OLED和USB转串口等功能。
PM2.5系统测试:
连接好硬件,系统启动后加载模块到系统,运行应用程序spidev_test,将从arduino开发板上读到的PM2.5的数据发送到OLED上显示,视频里面我们可以看到PM2.5测试数据在不断刷新显示。
硬件准备:
Ricoboard开发板,OLED显示模块,PM2.5检测设备(Dust Sensor),USB转串口线和arduino开发板。
OLED和USB转串口接线方式见:
https://bbs.elecfans.com/forum.p ... =1101412&extra=和 https://bbs.elecfans.com/forum.p ... =1102312&extra=
软件实现:
1.OLED显示设备功能实现:主要参考 https://bbs.elecfans.com/forum.p ... =1101412&extra=,
2.PM2.5检测设备(Dust Sensor)功能实现,其主要实现逻辑是在arduino开发板上,自己并没有将其连接到Ricoboard开发板上,是特意为了尝试一下Ricoboard这类 arm开发板是否可以与arduino这类开发板整合起来,通过实验发现两者之间可以很好的结合在一起。USB转串口的实现参考:
https://bbs.elecfans.com/forum.p ... =1102312&extra=
3.应用程序功能实现:1)主要是USB转串口信息的读取;2)是如何访问OLED驱动的设备节点,如何将PM2.5的数据发送到OLED驱动中进行。
- /*
- * SPI testing utility (using spidev driver)
- *
- * Copyright (c) 2007 MontaVista Software, Inc.
- * Copyright (c) 2007 Anton Vorontsov
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License.
- *
- * Cross-compile with cross-gcc -I/path/to/cross-kernel/include
- */
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include
- #include /*文件控制定义*/
- #include /*PPSIX 终端控制定义*/
- #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
- static void pabort(const char *s)
- {
- perror(s);
- abort();
- }
- #define UART_BAND_RATE 115200
- static const char *device = "/dev/oeld32766.0";
- static uint8_t mode;
- static uint8_t bits = 8;
- static uint32_t speed = 500000;
- static uint16_t delay;
- struct ss1306_data {
- int x1;
- int y1;
- int str1_len;
- char str1[20];
- int x2;
- int y2;
- char str2[20];
- int str2_len;
- int x3;
- int y3;
- char str3[20];
- int str3_len;
- };
- #if 1
- static void transfer1(int fd, int x1, int y1, char *str1, int str1_len,
- int x2, int y2, char *str2, int str2_len,
- int x3, int y3, char *str3, int str3_len)
- {
- int ret;
- //char str1[] = "1234567890abcdefg";
- //char str2[] = "asdcfgtrerrefd";
- struct ss1306_data tx;
- struct ss1306_data *ptx;
- ptx = &tx;
- tx.x1 = 0;
- tx.y1 = 0;
- memcpy(tx.str1, str1, str1_len);
- tx.x2 = 0;
- tx.y2 = 16;
- memcpy(tx.str2, str2, str2_len);
- tx.x3 = 0;
- tx.y3 = 32;
- memcpy(tx.str3, str3, str3_len);
- // printf("==str1=%s, str2=%s str3=%sn", str1, str2, str3);
- ret = write(fd, &tx, sizeof(struct ss1306_data));
- if(ret > 0)
- {
- printf("=2=strlen(str1)=%d, sizeof(struct ss1306_data)=%d n", str1_len, sizeof(struct ss1306_data));
- }
- else
- {
- printf("show sucess!n");
- // printf("=3=strlen(str1)=%d, sizeof(struct ss1306_data)=%d n", str1_len, sizeof(struct ss1306_data));
- }
- }
- #else
- static void transfer(int fd)
- {
- int ret;
- uint8_t tx[] = {
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0x40, 0x00, 0x00, 0x00, 0x00, 0x95,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
- 0xDE, 0xAD, 0xBE, 0xEF, 0xBA, 0xAD,
- 0xF0, 0x0D,
- };
- uint8_t rx[ARRAY_SIZE(tx)] = {0, };
- struct spi_ioc_transfer tr = {
- .tx_buf = (unsigned long)tx,
- .rx_buf = (unsigned long)rx,
- .len = ARRAY_SIZE(tx),
- .delay_usecs = delay,
- .speed_hz = speed,
- .bits_per_word = bits,
- };
- ret = ioctl(fd, SPI_IOC_MESSAGE(1), &tr);
- if (ret < 1)
- pabort("can't send spi message");
- for (ret = 0; ret < ARRAY_SIZE(tx); ret++) {
- if (!(ret % 6))
- puts("");
- printf("%.2X ", rx[ret]);
- }
- puts("");
- }
- #endif
- #define FALSE -1
- #define TRUE 0
-
- /*******************************************************************
- * 名称: UART0_Open
- * 功能: 打开串口并返回串口设备文件描述
- * 入口参数: fd :文件描述符 port :串口号(ttyS0,ttyS1,ttyS2)
- * 出口参数: 正确返回为1,错误返回为0
- *******************************************************************/
- int UART0_Open(int fd,char* port)
- {
-
- fd = open( port, O_RDWR|O_NOCTTY|O_NDELAY);
- if (FALSE == fd)
- {
- perror("Can't Open Serial Port");
- return(FALSE);
- }
- //恢复串口为阻塞状态
- if(fcntl(fd, F_SETFL, 0) < 0)
- {
- printf("fcntl failed!n");
- return(FALSE);
- }
- else
- {
- printf("fcntl=%dn",fcntl(fd, F_SETFL,0));
- }
- //测试是否为终端设备
- if(0 == isatty(STDIN_FILENO))
- {
- printf("standard input is not a terminal devicen");
- return(FALSE);
- }
- else
- {
- printf("isatty success!n");
- }
- printf("fd->open=%dn",fd);
- return fd;
- }
- /*******************************************************************
- * 名称: UART0_Close
- * 功能: 关闭串口并返回串口设备文件描述
- * 入口参数: fd :文件描述符 port :串口号(ttyS0,ttyS1,ttyS2)
- * 出口参数: void
- *******************************************************************/
-
- void UART0_Close(int fd)
- {
- close(fd);
- }
-
- /*******************************************************************
- * 名称: UART0_Set
- * 功能: 设置串口数据位,停止位和效验位
- * 入口参数: fd 串口文件描述符
- * speed 串口速度
- * flow_ctrl 数据流控制
- * databits 数据位 取值为 7 或者8
- * stopbits 停止位 取值为 1 或者2
- * parity 效验类型 取值为N,E,O,,S
- *出口参数: 正确返回为1,错误返回为0
- *******************************************************************/
- int UART0_Set(int fd,int speed,int flow_ctrl,int databits,int stopbits,int parity)
- {
-
- int i;
- int status;
- int speed_arr[] = { B115200, B57600, B38400, B19200, B9600, B4800, B2400, B1200, B300};
- int name_arr[] = {115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200, 300};
-
- struct termios options;
-
- /*tcgetattr(fd,&options)得到与fd指向对象的相关参数,并将它们保存于options,该函数还可以测试配置是否正确,该串口是否可用等。若调用成功,函数返回值为0,若调用失败,函数返回值为1.
- */
- if ( tcgetattr( fd,&options) != 0)
- {
- perror("SetupSerial 1");
- return(FALSE);
- }
-
- //设置串口输入波特率和输出波特率
- for ( i= 0; i < sizeof(speed_arr) / sizeof(int); i++)
- {
- if (speed == name_arr[i])
- {
- cfsetispeed(&options, speed_arr[i]);
- cfsetospeed(&options, speed_arr[i]);
- }
- }
-
- //修改控制模式,保证程序不会占用串口
- options.c_cflag |= CLOCAL;
- //修改控制模式,使得能够从串口中读取输入数据
- options.c_cflag |= CREAD;
-
- //设置数据流控制
- switch(flow_ctrl)
- {
-
- case 0 ://不使用流控制
- options.c_cflag &= ~CRTSCTS;
- break;
-
- case 1 ://使用硬件流控制
- options.c_cflag |= CRTSCTS;
- break;
- case 2 ://使用软件流控制
- options.c_cflag |= IXON | IXOFF | IXANY;
- break;
- }
- //设置数据位
- //屏蔽其他标志位
- options.c_cflag &= ~CSIZE;
- switch (databits)
- {
- case 5 :
- options.c_cflag |= CS5;
- break;
- case 6 :
- options.c_cflag |= CS6;
- break;
- case 7 :
- options.c_cflag |= CS7;
- break;
- case 8:
- options.c_cflag |= CS8;
- break;
- default:
- fprintf(stderr,"Unsupported data sizen");
- return (FALSE);
- }
- //设置校验位
- switch (parity)
- {
- case 'n':
- case 'N': //无奇偶校验位。
- options.c_cflag &= ~PARENB;
- options.c_iflag &= ~INPCK;
- break;
- case 'o':
- case 'O'://设置为奇校验
- options.c_cflag |= (PARODD | PARENB);
- options.c_iflag |= INPCK;
- break;
- case 'e':
- case 'E'://设置为偶校验
- options.c_cflag |= PARENB;
- options.c_cflag &= ~PARODD;
- options.c_iflag |= INPCK;
- break;
- case 's':
- case 'S': //设置为空格
- options.c_cflag &= ~PARENB;
- options.c_cflag &= ~CSTOPB;
- break;
- default:
- fprintf(stderr,"Unsupported parityn");
- return (FALSE);
- }
- // 设置停止位
- switch (stopbits)
- {
- case 1:
- options.c_cflag &= ~CSTOPB; break;
- case 2:
- options.c_cflag |= CSTOPB; break;
- default:
- fprintf(stderr,"Unsupported stop bitsn");
- return (FALSE);
- }
-
- //修改输出模式,原始数据输出
- options.c_oflag &= ~OPOST;
-
- options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);//我加的
- //options.c_lflag &= ~(ISIG | ICANON);
- {// 0x11 send
- options.c_lflag &= ~(ICANON );
- options.c_lflag &= ~(ICANON |ISIG);
- options.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
- }
- //设置等待时间和最小接收字符
- options.c_cc[VTIME] = 0; /* 读取一个字符等待1*(1/10)s */
- options.c_cc[VMIN] = 1; /* 读取字符的最少个数为1 */
-
- //如果发生数据溢出,接收数据,但是不再读取 刷新收到的数据但是不读
- tcflush(fd,TCIFLUSH);
-
- //激活配置 (将修改后的termios数据设置到串口中)
- if (tcsetattr(fd,TCSANOW,&options) != 0)
- {
- perror("com set error!n");
- return (FALSE);
- }
- return (TRUE);
- }
- /*******************************************************************
- * 名称: UART0_Init()
- * 功能: 串口初始化
- * 入口参数: fd : 文件描述符
- * speed : 串口速度
- * flow_ctrl 数据流控制
- * databits 数据位 取值为 7 或者8
- * stopbits 停止位 取值为 1 或者2
- * parity 效验类型 取值为N,E,O,,S
- *
- * 出口参数: 正确返回为1,错误返回为0
- *******************************************************************/
- int UART0_Init(int fd, int speed,int flow_ctrl,int databits,int stopbits,int parity)
- {
- int err;
- //设置串口数据帧格式
- if (UART0_Set(fd,UART_BAND_RATE,0,8,1,'N') == FALSE)
- {
- return FALSE;
- }
- else
- {
- return TRUE;
- }
- }
- unsigned int time_count = 0;
-
- /*******************************************************************
- * 名称: UART0_Recv
- * 功能: 接收串口数据
- * 入口参数: fd :文件描述符
- * rcv_buf :接收串口中数据存入rcv_buf缓冲区中
- * data_len :一帧数据的长度
- * 出口参数: 正确返回为1,错误返回为0
- *******************************************************************/
- int UART0_Recv(int fd, char *rcv_buf,int data_len)
- {
- int len,fs_sel;
- fd_set fs_read;
-
- struct timeval time;
-
- FD_ZERO(&fs_read);
- FD_SET(fd,&fs_read);
-
- // time.tv_sec = 1;
- // time.tv_usec = 0;
- // time_count += time.tv_sec;
-
- //使用select实现串口的多路通信
- fs_sel = select(fd+1,&fs_read,NULL,NULL, NULL);//&time);
- if(fs_sel)
- {
- len = read(fd,rcv_buf,data_len);
- // printf("I am right!(version1.2) len = %d fs_sel = %dn",len,fs_sel);
- return len;
- }
- else
- {
- // printf("Sorry,I am wrong!");
- return FALSE;
- }
- }
- /********************************************************************
- * 名称: UART0_Send
- * 功能: 发送数据
- * 入口参数: fd :文件描述符
- * send_buf :存放串口发送数据
- * data_len :一帧数据的个数
- * 出口参数: 正确返回为1,错误返回为0
- *******************************************************************/
- int UART0_Send(int fd, char *send_buf,int data_len)
- {
- int len = 0;
-
- len = write(fd,send_buf,data_len);
- if (len == data_len )
- {
- return len;
- }
- else
- {
-
- tcflush(fd,TCOFLUSH);
- return FALSE;
- }
-
- }
- static void print_usage(const char *prog)
- {
- printf("Usage: %s [-D***dlHOLC3]n", prog);
- puts(" -D --device device to use (default /dev/spidev1.1)n"
- " -s --speed max speed (Hz)n"
- " -d --delay delay (usec)n"
- " -b --bpw bits per word n"
- " -l --loop loopbackn"
- " -H --cpha clock phasen"
- " -O --cpol clock polarityn"
- " -L --l*** least significant bit firstn"
- " -C --cs-high chip select active highn"
- " -3 --3wire SI/SO signals sharedn");
- exit(1);
- }
- static void parse_opts(int argc, char *argv[])
- {
- while (1) {
- static const struct option lopts[] = {
- { "device", 1, 0, 'D' },
- { "speed", 1, 0, 's' },
- { "delay", 1, 0, 'd' },
- { "bpw", 1, 0, 'b' },
- { "loop", 0, 0, 'l' },
- { "cpha", 0, 0, 'H' },
- { "cpol", 0, 0, 'O' },
- { "l***", 0, 0, 'L' },
- { "cs-high", 0, 0, 'C' },
- { "3wire", 0, 0, '3' },
- { "no-cs", 0, 0, 'N' },
- { "ready", 0, 0, 'R' },
- { NULL, 0, 0, 0 },
- };
- int c;
- c = getopt_long(argc, argv, "D:s:d:b:lHOLC3NR", lopts, NULL);
- if (c == -1)
- break;
- switch (c) {
- case 'D':
- device = optarg;
- break;
- case 's':
- speed = atoi(optarg);
- break;
- case 'd':
- delay = atoi(optarg);
- break;
- case 'b':
- bits = atoi(optarg);
- break;
- case 'l':
- mode |= SPI_LOOP;
- break;
- case 'H':
- mode |= SPI_CPHA;
- break;
- case 'O':
- mode |= SPI_CPOL;
- break;
- case 'L':
- mode |= SPI_LSB_FIRST;
- break;
- case 'C':
- mode |= SPI_CS_HIGH;
- break;
- case '3':
- mode |= SPI_3WIRE;
- break;
- case 'N':
- mode |= SPI_NO_CS;
- break;
- case 'R':
- mode |= SPI_READY;
- break;
- default:
- print_usage(argv[0]);
- break;
- }
- }
- }
- #define UART_ONCE_RXBUF_SIZE 1024
- #define UART_MAX_RXBUF_SIZE 4096
- #define GPS_DATA_MAX_LEN 1400
- char uart_once_buf[UART_ONCE_RXBUF_SIZE];
- char uart_rcv_buf[UART_MAX_RXBUF_SIZE];
- int main(int argc, char *argv[])
- {
- int ret = 0;
- int fd;
- int fdu***0;
- int uart_wr_ptr = 0;
- int len,fs_sel;
- fd_set fs_read;
- struct timeval time;
- int first_start = 1;
- int i = 0;
-
-
- parse_opts(argc, argv);
- fd = open(device, O_RDWR);
- if (fd < 0)
- pabort("can't open device");
- /*
- * spi mode
- */
- ret = ioctl(fd, SPI_IOC_WR_MODE, &mode);
- if (ret == -1)
- pabort("can't set spi mode");
- ret = ioctl(fd, SPI_IOC_RD_MODE, &mode);
- if (ret == -1)
- pabort("can't get spi mode");
- /*
- * bits per word
- */
- ret = ioctl(fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
- if (ret == -1)
- pabort("can't set bits per word");
- ret = ioctl(fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
- if (ret == -1)
- pabort("can't get bits per word");
- /*
- * max speed hz
- */
- ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
- if (ret == -1)
- pabort("can't set max speed hz");
- ret = ioctl(fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
- if (ret == -1)
- pabort("can't get max speed hz");
- printf("spi mode: %dn", mode);
- printf("bits per word: %dn", bits);
- printf("max speed: %d Hz (%d KHz)n", speed, speed/1000);
- fdu***0 = UART0_Open(fd,"/dev/ttyUSB0"); //打开串口,返回文件描述符
- do {
- ret = UART0_Init(fdu***0,UART_BAND_RATE,0,8,1,'N');
- printf("Set Port Exactly!n");
- }while (FALSE == ret || FALSE == fdu***0);
- uart_wr_ptr = 0;
-
- while (1) //循环读取数据
- {
- // len = UART0_Recv(fd, uart_once_buf, 1024);
- FD_ZERO(&fs_read);
- FD_SET(fdu***0,&fs_read);
- time.tv_sec = 0;
- time.tv_usec = 1000;
- //使用select实现串口的多路通信
- fs_sel = select(fdu***0+1,&fs_read,NULL,NULL,&time);
- if (fs_sel)
- {
- len = read(fdu***0,uart_once_buf,1024);
- // printf("once read =%d uart_once_buf=%sn", len, uart_once_buf);
- if (len > 0)
- {
- if (first_start)
- {
- first_start = 0;
- }
- memcpy(&uart_rcv_buf[uart_wr_ptr], uart_once_buf, len);
- uart_wr_ptr += len;
- }
- }
- else
- {
- // printf("time out!n");
- if (first_start == 0)
- {
- if (len > 0)
- {
- if (uart_wr_ptr > 0)
- {
- printf("uart_rcv_buf=%s uart_wr_ptr=%dn", uart_rcv_buf, uart_wr_ptr);
- if (uart_wr_ptr < 1514)
- {
- // printf("tv_usec = %xn", tv.tv_usec);
- // memcpy(gps_pkt_p->gps_data, uart_rcv_buf, uart_wr_ptr);
- char str1[16] = {0};
- char str2[16] = {0};
- char str3[64] = {0};
- if(uart_wr_ptr > 16)
- {
- memcpy(str1, uart_rcv_buf, 16);
- if(uart_wr_ptr > 16 + 16)
- memcpy(str2, uart_rcv_buf+16, 16);
- if(uart_wr_ptr > 16 * 2)
- {
- int n3 = 0;
- if(uart_wr_ptr >= 16 * 3)
- {
- n3 = 16;
- }
- else
- {
- n3 = uart_wr_ptr - 32;
- }
- for(i = 0; i < n3; i++)
- {
- //printf("uart_wr_ptr[%d]=0x%xn", i+32, uart_rcv_buf[i+32]);
- if(uart_rcv_buf[i+32] == 0xa)
- uart_rcv_buf[i+32] = '\0';
- }
- memcpy(str3, uart_rcv_buf+32, n3);
- }
- transfer1(fd, 0, 0, str1, 16,
- 0, 16, str2, 16,
- 0, 32, str3, 16);
- }
- // Send ethernet frame to socket.
-
- }
- uart_wr_ptr = 0;
- }
- len = 0;
- }
- }
- }
-
-
-
- }
- close(fdu***0);
- close(fd);
- return ret;
- }
复制代码
4.OELD驱动添加设备节点读写功能,只贴出关键代码。
1)驱动初始化里面将oeld_fops注册到字符驱动中
- static int __init oeld_init(void)
- {
- int status;
- /* Claim our 256 reserved device numbers. Then register a class
- * that will key udev/mdev to add/remove /dev nodes. Last, register
- * the driver which manages those device numbers.
- */
- printk("=1=oeld_initn");
- BUILD_BUG_ON(N_SPI_MINORS > 256);
- printk("=2=oeld_initn");
- status = register_chrdev(oeld_MAJOR, "spi", &oeld_fops);
- if (status < 0)
- return status;
- printk("=3=oeld_initn");
- #ifdef CONFIG_COMPAT
- printk("=3=oeld_init CONFIG_COMPAT definen");
- #endif
- oeld_class = class_create(THIS_MODULE, "oeld");
- if (IS_ERR(oeld_class)) {
- unregister_chrdev(oeld_MAJOR, oeld_spi_driver.driver.name);
- return PTR_ERR(oeld_class);
- }
- printk("=4=oeld_initn");
- status = spi_register_driver(&oeld_spi_driver);
- if (status < 0) {
- class_destroy(oeld_class);
- unregister_chrdev(oeld_MAJOR, oeld_spi_driver.driver.name);
- }
- printk("=5=oeld_initn");
- return status;
- }
- module_init(oeld_init);
复制代码
2)修改wirte实现函数,将应用程序发送的数据显示处理。
- static ssize_t
- oeld_write(struct file *filp, const char __user *buf,
- size_t count, loff_t *f_pos)
- {
- struct oeld_data *oeld;
- ssize_t status = 0;
- unsigned long missing;
- struct ss1306_data *pss1306_data;
- // printk("=1=oeld_write count=%d, bufsiz=%dn", count, bufsiz);
- /* chipselect only toggles at start or end of operation */
- if (count > bufsiz)
- return -EMSGSIZE;
- // printk("=2=oeld_write count=%d, bufsiz=%dn", count, bufsiz);
- oeld = filp->private_data;
- mutex_lock(&oeld->buf_lock);
- missing = copy_from_user(oeld->buffer, buf, count);
- if (missing == 0) {
- #if 0
- status = oeld_sync_write(oeld, count);
- #else
- // printk("=211=oeld_write count=%d, bufsiz=%dn", count, bufsiz);
- pss1306_data = (struct ss1306_data *)oeld->buffer;
- status = ssd1306_showString(oeld->spi, pss1306_data);
- // printk("=212=oeld_write count=%d, status=%dn", count, status);
- #endif
- } else
- {
- printk("=22=oeld_write count=%d, bufsiz=%dn", count, bufsiz);
- status = -EFAULT;
- }
- mutex_unlock(&oeld->buf_lock);
- return status;
- }
复制代码
项目总结:
1.通过此次试用自己更加清晰的理解了GPIO,EEPROM,RTC等功能;
2.对于三线SPI模式的OLED设备有了一定的了解,美中不足的是4线SPI模式和IIC模式的OLED模块尚未实现功能,后面有时间继续努力。
3.按照计划自己还会测试另外一个RTC模块,后面时间一定将这个功能补上。
测试视频见下面的链接:
0
|
|
|
|