之前说过只要能控制高低电平就可以控制电机的正反转,我想通过修改友善官方的LED驱动程序来达到控制GPIO高低电平的目的,但是看了很久都没有怎么看懂,就去网上找看有没有比较好理解的程序,找到一个,原帖地址如下,十分感谢intel版主的无私分享:
原帖作者的小车是通过左右轮子的差速来实现转向的,可我的小车是后轮驱动,靠前轮转向的,所以对源程序做了些修改:
C代码
#include
#include
#include
//#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#include
#include
#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
{
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测试程序进行了修改,修改的程序如下:
C代码
#include
#include
#include
#include
#include
#include
#include
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 9 || led_no 3) {
fprintf(stderr, "Usage: car led_no 0|1\n");
exit(1);
}
fd = open("/dev/car0", 0);
if (fd
fd = open("/dev/car", 0);
}
if (fd
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案例开发指南》按照上面的例程修改的,源码如下:
C代码
/*
* main.c
*
* Created on: 2012-10-18
* Author: micro
*/
#include
#include
#include
#include
#include
#include
#include
#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 '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
fd = open("/dev/car", 0);
}
if (fd
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、手动安装驱动与运行服务器程序等不足,这些都是我接下来所要解决的,另外还有块难啃的骨头——视频实时传输……谢谢大家对于我的关注,我会继续努力~~~