之前说过只要能控制高低电平就可以控制电机的正反转,我想通过修改友善官方的LED驱动程序来达到控制GPIO高低电平的目的,但是看了很久都没有怎么看懂,就去网上找看有没有比较好理解的程序,找到一个,原帖地址如下,十分感谢intel版主的无私分享:
http://www.arm9home.net/read.php?tid-15941.html
原帖作者的小车是通过左右轮子的差速来实现转向的,可我的小车是后轮驱动,靠前轮转向的,所以对源程序做了些修改:
- #include <linux/miscdevice.h>
- #include <linux/delay.h>
- #include <asm/irq.h>
- //#include <mach/regs-gpio.h>
- #include <mach/hardware.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/init.h>
- #include <linux/mm.h>
- #include <linux/fs.h>
- #include <linux/types.h>
- #include <linux/delay.h>
- #include <linux/moduleparam.h>
- #include <linux/slab.h>
- #include <linux/errno.h>
- #include <linux/ioctl.h>
- #include <linux/cdev.h>
- #include <linux/string.h>
- #include <linux/list.h>
- #include <linux/pci.h>
- #include <asm/uaccess.h>
- #include <asm/atomic.h>
- #include <asm/unistd.h>
- #include <mach/map.h>
- #include <mach/regs-clock.h>
- #include <mach/regs-gpio.h>
- #include <mach/gpio.h>
- //#include <linux/gpio.h>
- #include <plat/gpio-cfg.h>
- #include <mach/gpio-bank-e.h>
- #include <mach/gpio-bank-k.h>
- #define DEVICE_NAME "car"
- //控制前后的电机
- #define MOTOR_QH_ENA S3C64XX_GPM(4)
- #define MOTOR_QH_ENB S3C64XX_GPM(5)
- //控制方向的电机
- #define MOTOR_FX_ENA S3C64XX_GPE(3)
- #define MOTOR_FX_ENB S3C64XX_GPE(4)
- #define debug 1
- static unsigned long motor_table [] = {
- MOTOR_QH_ENA,
- MOTOR_QH_ENB,
- MOTOR_FX_ENA,
- MOTOR_FX_ENB
- };
- //初始化GPIO口
- static int car_motor_init(void)
- {
- int ret=0,i;
- for (i = 0; i < 4; i++)
- {
- if(gpio_is_valid(motor_table[i])==-EINVAL)
- {
- printk("ERROR,GPIO used by other devices ! \n");
- break;
- }
- //上拉GPIO
- s3c_gpio_setpull(motor_table[i], S3C_GPIO_PULL_UP);
- //设置为输出
- s3c_gpio_cfgpin(motor_table[i], S3C_GPIO_OUTPUT);
- //设置默认值为低电平
- gpio_set_value(motor_table[i],0);
- }
- return ret;
- };
- static void car_motor_status(void)
- {
- printk("MOTOR_QH_ENA=%d\nMOTOR_QH_ENB=%d\nMOTOR_FX_ENA=%d\nMOTOR_FX_ENB=%d\n",
- gpio_get_value(MOTOR_QH_ENA),
- gpio_get_value(MOTOR_QH_ENB),
- gpio_get_value(MOTOR_FX_ENA),
- gpio_get_value(MOTOR_FX_ENB));
- };
- static void car_motor_run(void)
- {
- gpio_set_value(MOTOR_QH_ENA,1);
- gpio_set_value(MOTOR_QH_ENB,0);
- };
- static void car_motor_stop(void)
- {
- #if debug
- car_motor_status();
- #endif
- gpio_set_value(MOTOR_QH_ENA,0);
- gpio_set_value(MOTOR_QH_ENB,0);
- };
- static void car_motor_back(void)
- {
- gpio_set_value(MOTOR_QH_ENA,0);
- gpio_set_value(MOTOR_QH_ENB,1);
- };
- static void car_motor_left(void)
- {
- gpio_set_value(MOTOR_FX_ENA,1);
- gpio_set_value(MOTOR_FX_ENB,0);
- };
- static void car_motor_right(void)
- {
- gpio_set_value(MOTOR_FX_ENA,0);
- gpio_set_value(MOTOR_FX_ENB,1);
- };
- static void car_motor_zheng(void)
- {
- gpio_set_value(MOTOR_FX_ENA,0);
- gpio_set_value(MOTOR_FX_ENB,0);
- };
- static long car_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
- {
- #if debug
- printk("cmd=%d\n",cmd);
- #endif
- long ret=0;
- switch(cmd)
- {
- case '0': case 0:
- //小车停止
- car_motor_stop();
- break;
- case '1': case 1:
- //小车前进
- car_motor_run();
- break;
- //小车后退
- case '2': case 2:
- car_motor_back();
- break;
- //左转
- case '3': case 3:
- car_motor_left();
- break;
- //右转
- case '4': case 4:
- car_motor_right();
- break;
- //直走
- case '5': case 5:
- car_motor_zheng();
- break;
- //小车后退
- case '6': case 6:
- car_motor_back();
- break;
- return 0;
- default:
- return -EINVAL;
- }
- return ret;
- }
- static struct file_operations dev_fops = {
- .owner = THIS_MODULE,
- .unlocked_ioctl = car_ioctl,
- };
- static struct miscdevice misc = {
- .minor = MISC_DYNAMIC_MINOR,
- .name = DEVICE_NAME,
- .fops = &dev_fops,
- };
- static int __init dev_init(void)
- {
- int ret;
- car_motor_init();
- // car_motor_run();
- ret = misc_register(&misc);
- printk (DEVICE_NAME"\tinitialized\n");
- return ret;
- }
- static void __exit dev_exit(void)
- {
- misc_deregister(&misc);
- }
- module_init(dev_init);
- module_exit(dev_exit);
- MODULE_LICENSE("GPL");
- MODULE_AUTHOR("Lintel.");
为了测试驱动,根据友善的LED测试程序进行了修改,修改的程序如下:
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <sys/ioctl.h>
- #include <sys/types.h>
- #include <sys/stat.h>
- #include <fcntl.h>
- int main(int argc, char **argv)
- {
- int cmd;
- int led_no;
- int fd;
- if (argc != 3 || sscanf(argv[1], "%d", &led_no) != 1 || sscanf(argv[2],"%d", &cmd) != 1 ||
- cmd < 0 || cmd > 9 || led_no < 0 || led_no > 3) {
- fprintf(stderr, "Usage: car led_no 0|1\n");
- exit(1);
- }
- fd = open("/dev/car0", 0);
- if (fd < 0) {
- fd = open("/dev/car", 0);
- }
- if (fd < 0) {
- perror("open device car");
- exit(1);
- }
- printf("CMD=%d\n",cmd);
- ioctl(fd, cmd, led_no);
- close(fd);
- return 0;
- }
这个测试程序有个小问题,当传入的参数为‘0’‘1’时没有问题,但是当参数为‘2’时,怎么也传不进去,所以我把驱动代码的‘6’设置为了和‘2’一样的行为。
下面进行连线,按照驱动的GPIO口连接到电机驱动模块上,因为没有买到间距为2mm的接口,先用一根一根的线代替了,小车现在是这个样子:
接下来马上就进行UDP通信部分,我也买了友善的SDWIFI模块,插上就能用,按照文档连上了我的路由器,用ifconfig命令查看开发板ip,开发板和笔记本互相能ping通,接下来就是写程序来监听指定端口上得到的数据并做出相应的反应,这部分不是很难,我是借了本书《嵌入式Linux案例开发指南》按照上面的例程修改的,源码如下:
- /*
- * main.c
- *
- * Created on: 2012-10-18
- * Author: micro
- */
- #include<sys/types.h>
- #include<sys/socket.h>
- #include<string.h>
- #include<netinet/in.h>
- #include<stdio.h>
- #include<stdlib.h>
- #include <sys/ioctl.h>
- #define MAXLINE 5
- #define SERV_PORT 12345
- void do_echo(int sockfd, struct sockaddr *pcliaddr, socklen_t clilen) {
- int n;
- socklen_t len;
- char mesg[MAXLINE];
- for (;;) {
- len = clilen;
- printf("Waiting for Data...\n");
- //等待数据
- n = recvfrom(sockfd, mesg, MAXLINE, 0, pcliaddr, &len);
- //将数据返回
- //sendto(sockfd, mesg, n, 0, pcliaddr, len);
- //打印数据
- //printf("%s\n", mesg);
- colcars(mesg[0]);
- }
- }
- //根据收到的信息执行操作
- void colcars(char cmd) {
- if (cmd < '0' || cmd > '10') {
- printf("cmd error\n");
- } else {
- if (cmd == '2') {
- // printf("car back\n");
- car_col('6');
- } else {
- car_col(cmd);
- }
- }
- }
- void car_col(char cmd) {
- int fd;
- fd = open("/dev/car0", 0);
- if (fd < 0) {
- fd = open("/dev/car", 0);
- }
- if (fd < 0) {
- perror("open device car");
- return;
- }
- ioctl(fd, cmd, 0);
- close(fd);
- }
- int main(void) {
- int sockfd;
- struct sockaddr_in servaddr, cliaddr;
- //建立socket
- sockfd = socket(AF_INET, SOCK_DGRAM, 0);
- //初始化服务器地址
- bzero(&servaddr, sizeof(servaddr));
- servaddr.sin_family = AF_INET;
- servaddr.sin_addr.s_addr = htonl(INADDR_ANY );
- servaddr.sin_port = htons(SERV_PORT);
- //为socket绑定端口
- if (bind(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr)) == -1) {
- perror("bind error");
- exit(1);
- }
- //执行服务器程序
- do_echo(sockfd, (struct sockaddr *) &cliaddr, sizeof(cliaddr));
- return 0;
- }
至此,我已经基本把我之前的学习内容总结了一遍,我的小车已经基本实现了通过WiFi来远程控制前后左右运动的目的。但是现在我还不会用PWM信号来调速或者控制舵机,而且现在的程序相对粗糙,控制协议也很简单,还存在着每次重启开发板都要手动连接wifi、手动安装驱动与运行服务器程序等不足,这些都是我接下来所要解决的,另外还有块难啃的骨头——视频实时传输……谢谢大家对于我的关注,我会继续努力~~~