之前说过只要能控制高低电平就可以控制电机的正反转,我想通过修改友善官方的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、手动安装驱动与运行服务器程序等不足,这些都是我接下来所要解决的,另外还有块难啃的骨头——视频实时传输……谢谢大家对于我的关注,我会继续努力~~~
传送门:
WiFi遥控小车(一):基于wicam模块的小车
WiFi遥控小车(二):选择学习&开发平台
WiFi遥控小车(三):搭建嵌入式Linux开发环境
WiFi遥控小车(四):简单直流电机驱动及UDP通信程序