课程设计开发笔记(已完成)

这个笔记记录的是ADDA模数转换存minisql的课程设计开发历程。

(额外添加了minigui实时显示)

涉及环境

GY-39光照采集模块

用的是粤嵌6818开发板,用的串口是tty1,开发环境是18.04Ubuntu,arm-Linux-gcc版本是5.20

Socket设计

CS模型中CS走的是TCPIP协议,默认的IP地址是192.168.10.113,端口号是8000

server会自动启动insertinsert会自启动qvfb,默认是在同一路径下。会创建一个data.txt

minisql设计

编译要用到好几个链接库,编译建议把编译库直接拖进去,插入的数据库是下文的要自己先手动创建。

1
create table sunlight (shine int)

之后minisql会启动qvfb。

minigui设计

minigui默认分辨率是*800600,显示数据来源并非数据库,而是通路径下的gui.txt**。

选题思路:

一开选题的时候考虑到,首先此题目的技术构成较为简单,组员这组长的指导下有完成的可能,其次是我对这个题目比较感兴趣,又恰好华为杯的选题和这个题目不谋而合,于是就初步敲定了这个题目。在和组员经过简短的沟通与交流后,最终决定用这个题目作为我们组的课程设计。

开发思路:

由于此题是先由GY-39采集到光照数据,然后经由socket将数据发送到宿主机的数据库中存储。初步判断主要分为三个模块:GY-39驱动模块,套接字数据传输模块以及minisql存储模块。

又由于其中使用的socket模块为C/S模型,可知此项目为松耦合的系统,可以三个模块并行开发。因此开发时三路并进,才得以在短时间内完成这个项目。

项目分工:

驱动开发工程师负责GY-39的驱动编写,网络工程师周晨曦负责C/S模型的搭建,数据库工程师李家伟负责数据的存储与管理。

问题汇总

由于是采用松散耦合并行开发的模式,各模块无可避免的要与文本文件打交道,其中最直观的问题是文本文件读取与变量赋值有着天然的差距。

int转string,string转int

一个看起来简单到不值一提的问题,但在实际开发中却困扰了我们很久,其中最大的疑惑是,各平台的文件存储格式,文件编码格式,文件分格符的差异。就比如说mac(Unix like)平台下文件IO是随意的,不管怎么都都不会读到可识别的字符以外的字符。Windows下文件io也相对松散。然而Ubuntu下和6818上的Linux kernel对文件存储有着近乎偏执的格式要求,必须严格按照不通平台不通存储,读取方式进行,一言不合,一点不对就全员乱码。

首先我们尝试了常规的open,read,write的文件io

1
2
3
4
5
6
int fd = open("data.txt", O_RDONLY);
char read_buf[4096];
char write_buf[100][10];
memset(write_buf, '\0', sizeof(write_buf));
int read_num = 0, write_row = 0, write_num = 0;
int res_read = (int)read(fd, read_buf, 4096);

发现每个变量中都会存在@^这个奇怪符号。

于是我们换成了标准的fopen,fread的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include "stdio.h"
void openfile() {
FILE* fp = fopen("test.txt", "rw");
if (fp == 0) printf("open file final");
char buffer[] = "hellowolrd";
char Buf[12];
fwrite(buffer, sizeof(char), sizeof(buffer), fp);
fread(Buf, sizeof(char), sizeof(buffer), fp);
for (int i = 0; i < sizeof(Buf); i++) printf("%c", Buf[i]);
fclose(fp);
}

int main(void) {
openfile();
return 0;
}

发现ubuntu和6818平台还是有@A的错误,会读到奇怪的,根本不能用的数据。好在redhat(宿主机可以用)。

后来我们又尝试了C++IO流的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void Rediect() {
string name;
cin >> name;
freopen("file.txt", "w", stdout);
cout << name << "\n";
freopen("file.txt", "r", stdin);//重定向到文件
cin >> name;
cerr << name << "\n";
freopen("/dev/tty", "w", stdout); //重定向到控制台
}

int main(void) {
Rediect();
cout << "hello wolrd \n" << endl;
return 0;
}

这次更离谱,直接不能用,数据都没有。

最终解决方案

echo大法好

最后想到了用system(“echo”)的方式解决了,由于各平台的echo都是所见即所得,因此不仅解决了乱码的问题,甚至分割数据都自动做好了(换行)。

1
2
3
4
5
void w_file(unsigned int n) {
char cmd[30] = {0};
sprintf(cmd, "echo %d >>data.txt", n);
system(cmd);
}

TCPIP(SOCKET)模块

问题与解决思路。

判断开启服务的端口号是否可用

1
netstat   -nultp

结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 0.0.0.0:32768 0.0.0.0:* LISTEN 3089/
tcp 0 0 127.0.0.1:32769 0.0.0.0:* LISTEN 3207/xinetd
tcp 0 0 0.0.0.0:32770 0.0.0.0:* LISTEN 3263/rpc.mountd
tcp 0 0 0.0.0.0:111 0.0.0.0:* LISTEN 3070/
tcp 0 0 0.0.0.0:6000 0.0.0.0:* LISTEN 3445/X
tcp 0 0 0.0.0.0:880 0.0.0.0:* LISTEN 3244/rpc.rquotad
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 3193/sshd
tcp 0 0 127.0.0.1:631 0.0.0.0:* LISTEN 3322/cupsd
tcp 0 0 127.0.0.1:25 0.0.0.0:* LISTEN 3283/
tcp 0 0 127.0.0.1:6010 0.0.0.0:* LISTEN 4036/sshd
udp 0 0 0.0.0.0:32768 0.0.0.0:* 3089/
udp 0 0 0.0.0.0:2049 0.0.0.0:* -
udp 0 0 0.0.0.0:32770 0.0.0.0:* -
udp 0 0 0.0.0.0:32771 0.0.0.0:* 3263/rpc.mountd
udp 0 0 0.0.0.0:69 0.0.0.0:* 3207/xinetd
udp 0 0 0.0.0.0:721 0.0.0.0:* 3089/
udp 0 0 0.0.0.0:877 0.0.0.0:* 3244/rpc.rquotad
udp 0 0 0.0.0.0:111 0.0.0.0:* 3070/
udp 0 0 0.0.0.0:631 0.0.0.0:* 3322/cupsd

这里可以看见我们socket绑定的8000端口是可用的。

服务器端口打不开,提示端口被占用

解决方法:查看此端口号是否开启复用,通常来说换个端口号即可解决。

无法ping通宿主机和修改ip地址

更改Ubuntu下的网卡ip

打开网卡配置文件

1
sudo vim /etc/network/interfaces 

更改如下配置

1
2
3
4
5
6
auto ens33
iface ens33 inet static
address 192.168.10.112 #ip地址
netmask 255.255.255.0 #子网掩码
gateway 192.168.10.12 #网关
dns-nameserver 114.114.114.114 #dns服务器

minigui

实时刷新函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
static void print_data(HDC hdc) {
int i = -1;
int buf1, buf2;
while (1) {
if (i == -1) read_data();
i++;
if (i < 10) {
buf1 = i;
//打印str_buf_1
TextOut(hdc, 60, 10 + i * 15, str_buf_1[buf1]);
sleep(1);
} else {
buf2 = i % 10;
TextOut(hdc, 60, 10 + buf2 * 15, str_buf_2[buf2]);
sleep(1);
if (i >= 20) {
SetPenColor(hdc, PIXEL_lightwhite);
FillBox(hdc, 0, 0, 640, 480);
i = -1;
//打印完两个数组后初始化tmp,在从str_buf_1开始
}
}
}
}

数据插入后自启动qvfb

insert.c部分代码:

1
2
3
4
5
if (qvfb_flag == 0) {
printf("AutoQvfb\n");
system("./AutoQvfb");
qvfb_flag = 1;
}

qvfb自启动代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include "stdio.h"
#include "stdlib.h"
#include "sys/types.h"
#include "sys/wait.h"

int child_funtion() {
if (execlp("qvfb", "qvfb", "-width", "800", "-height", "600", "-depth", "16",
NULL) < 0)
return 0;
else
// exit(EXIT_FAILURE);
return -1;
}

int main() {
pid_t child;
child = fork();
if (child == 0) {
child_funtion();
} else {
sleep(1);
system("./gui");
}
return 0;
}

msql

数据库创建

1
create table sunlight (shine int)

数据格式

1
./relshow sunlight

关于实验提供的插入代码这件事

由于实验提供的数据插入代码使用繁琐(有三到五个参数),并不适合日常使用,其次是代码逻辑不够清晰,所有初始化,插入,判断,复制等代码都在main函数中,修改维护起来十份困难,因此得大改已有的数据库插入代码。

修改后报段错误

原因时已有实验代码所用的count和cur参数导致的,在不明原因下num<count时依旧不会跳出循环,而是一定要跑满五千才跳出循环。

文件读取问题

由于采用的是并行开发逻辑,因此文件读取只能采用读取以收到的数据的文本文件,其中有很多问题,比如说编码问题和数据分隔问题呢,好在最后在阳老师的提示下想到了ASCLL的方法来判断数据是否是一组。

GY-39模块

老实说GY-39模块是最拖进度的部分,因为手里没有芯片顺丰到付也等了俩天才到,但这也让我有时间指导俩位新上任的网络工程师数据库工程师来熟悉他们的业务。后来在焊接GY-39引脚模块的时候出了点小差错,虚焊差点让这模块报废了也是心里一凉。

找不到GPIO口对应的引脚

通过查淘宝卖家提供的资料文档可以找到这样一段描述

1
2
3
4
5
开发板串口文件(cd/dev)
ttySAC0...>com5
ttySAC1...>com2
ttysAC2...>com3
ttySAC3...>com4

然后是CTDR引脚接反的问题,一开始没注意直接给他怼进去了,死活读不出数据一度怀疑卖家发了个坏的芯片。最后请教学长才得知这个俩个引脚接反了。

添加写入文件函数

由于这个模块的驱动代码我在九月份就写好了,但是一开始没学明白写的很乱,现在看着不敢动生怕坏了。最后花了点时间把功能分离才渐渐敢往里面添加写入函数。