有了硬件之后,就需要软件来控制。我在Tiny6410上使用的是linux系统,为了实现硬件控制,就得编写相应的驱动和软件。
在驱动方面,linux系统自身支持USB摄像头和Wifi网卡,只需要我编写电机驱动模块的驱动和D8253的驱动
在控制电机的时候,我使用了两路PWM时钟,这样我们通过改变PWM的占空比来实现速度控制。同时还得有一路GPIO来控制电机的方向。
可见电机驱动的关键就是配置两路PWM时钟
#define EFORCE_GPIO_PWM0 S3C64XX_GPF(14)
#define EFORCE_GPIO_PWM1 S3C64XX_GPF(15)
#define EFORCE_GPIO_DIR0 S3C64XX_GPN(4)
#define EFORCE_GPIO_DIR1 S3C64XX_GPN(5)
#define EFORCE_PWM_FREQ 10000
#define EFORCE_PWM_PRE 10
以上定义了两路PWM信号和两路控制信号的GPIO接口,PWM的频率和分频
void pwm0_stop( void )
{
s3c_gpio_cfgpin(EFORCE_GPIO_PWM0, S3C_GPIO_OUTPUT);
s3c_gpio_setpin(EFORCE_GPIO_PWM0, 0);
}
void pwm1_stop( void )
{
s3c_gpio_cfgpin(EFORCE_GPIO_PWM1, S3C_GPIO_OUTPUT);
s3c_gpio_setpin(EFORCE_GPIO_PWM1, 0);
}
以上两个函数为停车函数。如果我们想停止电机转动,就得使得PWM为0
static void set_pwm0( __u32 value)
{
unsigned long tcon;
unsigned long tcnt;
unsigned long tcfg1;
unsigned long tcfg0;
struct clk *clk_p;
unsigned long pclk;
s3c_gpio_cfgpin(EFORCE_GPIO_PWM0, S3C_GPIO_SFN(2));
tcon = __raw_readl(S3C_TCON);
tcfg1 = __raw_readl(S3C_TCFG1);
tcfg0 = __raw_readl(S3C_TCFG0);
tcfg0 &= ~S3C_TCFG_PRESCALER0_MASK;
tcfg0 |= (EFORCE_PWM_PRE - 1) << S3C_TCFG_PRESCALER0_SHIFT;
tcfg1 &= ~S3C_TCFG1_MUX0_MASK;
tcfg1 |= S3C_TCFG1_MUX0_DIV1;
__raw_writel(tcfg1, S3C_TCFG1);
__raw_writel(tcfg0, S3C_TCFG0);
clk_p = clk_get(NULL, "pclk");
pclk = clk_get_rate(clk_p);
tcnt = (pclk/EFORCE_PWM_PRE)/EFORCE_PWM_FREQ;
if (value >= tcnt) {
printk("PWM Warning: value too large\n");
value = tcnt - 1;
}
tcon &= ~(S3C_TCON_T0DEADZONE|S3C_TCON_T0INVERT|S3C_TCON_T0MANUALUPD|S3C_TCON_T0RELOAD|S3C_TCON_T0START);
if (value) {
__raw_writel(tcnt, S3C_TCNTB(0));
__raw_writel(value, S3C_TCMPB(0));
s3c_gpio_setpull(EFORCE_GPIO_PWM0, S3C_GPIO_PULL_NONE);
//disable deadzone, auto-reload, inv-off, update TCNTB0&TCMPB0, start timer 0
tcon |= S3C_TCON_T0MANUALUPD|S3C_TCON_T0RELOAD|S3C_TCON_T0START;
__raw_writel(tcon, S3C_TCON);
tcon &= ~S3C_TCON_T0MANUALUPD; //clear manual update bit
__raw_writel(tcon, S3C_TCON);
}
else {
pwm0_stop();
}
}
static void set_pwm1( __u32 value)
{
unsigned long tcon;
unsigned long tcnt;
unsigned long tcfg1;
unsigned long tcfg0;
struct clk *clk_p;
unsigned long pclk;
s3c_gpio_cfgpin(EFORCE_GPIO_PWM1, S3C_GPIO_SFN(2));
tcon = __raw_readl(S3C_TCON);
tcfg1 = __raw_readl(S3C_TCFG1);
tcfg0 = __raw_readl(S3C_TCFG0);
tcfg0 &= ~S3C_TCFG_PRESCALER0_MASK;
tcfg0 |= (EFORCE_PWM_PRE - 1) << S3C_TCFG_PRESCALER0_SHIFT;
tcfg1 &= ~S3C_TCFG1_MUX1_MASK;
tcfg1 |= S3C_TCFG1_MUX1_DIV1;
__raw_writel(tcfg1, S3C_TCFG1);
__raw_writel(tcfg0, S3C_TCFG0);
clk_p = clk_get(NULL, "pclk");
pclk = clk_get_rate(clk_p);
tcnt = (pclk/EFORCE_PWM_PRE)/EFORCE_PWM_FREQ;
if (value >= tcnt) {
printk("PWM Warning: value too large\n");
printk("PWM Warning: max value %lu\n", tcnt - 1);
value = tcnt - 1;
}
tcon &= ~(S3C_TCON_T1INVERT|S3C_TCON_T1MANUALUPD|S3C_TCON_T1RELOAD|S3C_TCON_T1START);
if (value) {
__raw_writel(tcnt, S3C_TCNTB(1));
__raw_writel(value, S3C_TCMPB(1));
s3c_gpio_setpull(EFORCE_GPIO_PWM0, S3C_GPIO_PULL_NONE);
tcon |= S3C_TCON_T1MANUALUPD|S3C_TCON_T1RELOAD|S3C_TCON_T1START;
__raw_writel(tcon, S3C_TCON);
tcon &= ~S3C_TCON_T1MANUALUPD; //clear manual update bit
__raw_writel(tcon, S3C_TCON);
}
else {
pwm1_stop();
}
}
以上两个函数用以设置PWM计数器的占空比。
#define D8253_TIMER0_PA 0x18000000
#define D8253_TIMER1_PA 0x18000001
#define D8253_TIMER2_PA 0x18000002
#define D8253_TIMERCTL_PA 0x18000003
#define D8253_DELAY 10
#define D8253_TIMER0_IRQ 19
#define D8253_TIMER1_IRQ 20
以上程序定义了D8253的地址 ,中断号。需要注意的是,D8253的地址取决于所在的片选信号,而且,D8253只有两个地址线,连到最低位上。
cs1 = __raw_readl(S3C64XX_SROM_BW) &
~(S3C64XX_SROM_BW__CS_MASK << S3C64XX_SROM_BW__NCS1__SHIFT);
cs1 |= ((0 << S3C64XX_SROM_BW__DATAWIDTH__SHIFT) |
(1 << S3C64XX_SROM_BW__WAITENABLE__SHIFT) |
(1 << S3C64XX_SROM_BW__BYTEENABLE__SHIFT)) <<
S3C64XX_SROM_BW__NCS1__SHIFT;
__raw_writel(cs1, S3C64XX_SROM_BW);
__raw_writel((0 << S3C64XX_SROM_BCX__PMC__SHIFT) |
(15 << S3C64XX_SROM_BCX__TACP__SHIFT) |
(1 << S3C64XX_SROM_BCX__TCAH__SHIFT) |
(1 << S3C64XX_SROM_BCX__TCOH__SHIFT) |
(24<< S3C64XX_SROM_BCX__TACC__SHIFT) |
(5 << S3C64XX_SROM_BCX__TCOS__SHIFT) |
(0 << S3C64XX_SROM_BCX__TACS__SHIFT), S3C64XX_SROM_BC1);
以上程序用以初始化S3C6410 SROM设置,使其支持D8253, 这些数值都要经过查阅D8253手册和S3C6410手册进行定值。
err = request_irq(IRQ_EINT(D8253_TIMER0_IRQ), timer_override, IRQ_TYPE_EDGE_RISING,
d8253_timer0_irq.name, (void *)&d8253_timer0_irq);
err = request_irq(IRQ_EINT(D8253_TIMER1_IRQ), timer_override, IRQ_TYPE_EDGE_RISING,
d8253_timer1_irq.name, (void *)&d8253_timer1_irq);
配置中断
case EFORCE_SET_TIMER:
disable_irq(IRQ_EINT(D8253_TIMER0_IRQ));
disable_irq(IRQ_EINT(D8253_TIMER1_IRQ));
if (!data.value0) {
data.value0 --;
}
if (!data.value1) {
data.value1 --;
}
// printk("value0: %u\tvalue1:%u\n", data.value0, data.value1);
tval0_h = data.value0 >> 16;
tval1_h = data.value1 >> 16;
tval0_l = data.value0 & 0xffff;
tval1_l = data.value1 & 0xffff;
writeb(0x30, d8253_ctl);
udelay(D8253_DELAY);
writeb(tval0_l & 0xff, d8253_timer0);
udelay(D8253_DELAY);
writeb(tval0_l >> 8, d8253_timer0);
udelay(D8253_DELAY);
writeb(0x70, d8253_ctl);
udelay(D8253_DELAY);
writeb(tval1_l & 0xff, d8253_timer1);
udelay(D8253_DELAY);
writeb(tval1_l >> 8, d8253_timer1);
udelay(D8253_DELAY);
enable_irq(IRQ_EINT(D8253_TIMER0_IRQ));
enable_irq(IRQ_EINT(D8253_TIMER1_IRQ));
break;
case EFORCE_GET_TIMER:
writeb(0x00, d8253_ctl);
udelay(D8253_DELAY);
data.time0 = jiffies;
tval0_l = readb(d8253_timer0);
udelay(D8253_DELAY);
tval0_l += readb(d8253_timer0) << 8;
udelay(D8253_DELAY);
writeb(0x40, d8253_ctl);
udelay(D8253_DELAY);
data.time1 = jiffies;
tval1_l = readb(d8253_timer1);
udelay(D8253_DELAY);
tval1_l += readb(d8253_timer1) << 8;
udelay(D8253_DELAY);
data.value0 = (tval0_h << 16) + tval0_l;
data.value1 = (tval1_h << 16) + tval1_l;
data.stop_flag = stop;
err = copy_to_user((d8253_data __user *)arg, &data, sizeof(d8253_data));
if (err) {
printk("error copy to user space");
return -EFAULT;
}
break;
设置和读取D8253数值
完整代码已上传资源里