基本知识:ARM一般会使用电平转换芯片把串口TTL电平转换成相应的232或485电平信号。
与232稍有不同的是,一般的485是半双工的,意思就是说同一时刻要么处于接收状态要么处于发送状态。
485芯片有许多种,但基本大同小异,一般有2个引脚给用户来控制收发状态。
当RE为低电平时,485芯片数据输入有效(低电平接收状态);当DE为高电平时,485芯片数据输出有效(高电平发送状态)。在半双工使用中,通常可以将这两个脚直接相连,然后通过输出的高低电平就可以让485芯片在接收和发送状态之间转换了。
方案:不管怎样的方案都需要一个485芯片,不同的是收发状态的控制,可以由电路本身控制,也可以接到CPU的IO口由驱动来控制。
1.电路本身控制:市面上可以买到的485转换器都使用这种方案,友善的开发板也使用这种方案,简单来讲,就是从485芯片的DI脚(接到ARM的TXD)取得收发状态,进而设置485芯片的收发状态。因为友善已经实现了,所以本文不讨论这种方案,友善的支持485的开发板光盘里有原理图,这里就不提供了。
2.IO
口控制:单片机很多使用此方案,因为实现起来简单,但是用在ARM
上就不简单了,本文就介绍此方案。单片机控制的话,大概是这么个流程:一开始是接收状态,数据发送前先置高,并稍作延时,等待485
芯片初始化为发送状态,然后开始发送,发送完成后稍作延时,等待发送完成,然后置低进入接收状态。在ARM
中虽然需要修改内核驱动,而且还要占用额外的IO
口,但仍然是一个可行的方案,经测试各种波特率都使用正常,连续几天使用过程中也挺稳定。详细请看具体实现方法。具体实现方法:485一般会用在定制的产品上,所以估计是Tiny6410用比较多吧?210的开发板还没有尝试,但一般2.6内核的话大同小异,如果有在210上改成功的,可以共享一下以供参考。
范例所用的开发板使用友善的Tiny6410,当然Mini6410也行,并且适用于android和linux的内核。
示范改2个串口为485,分别是COM0和COM2,因为COM1是5线串口改了可惜所以跳过。如果你懂得了如何改2个串口,那么改成1个或改成3个都没问题了吧。
这里举例用TI的485芯片:sn65lbc184,当然其它的485芯片也行。
芯片手册
http://wenku.baidu.com/view/32fbd336b90d6c85ec3ac6f7.html?st=1我用的电路如下图,COM0使用SPICS/GPC7来控制,COM2使用SPIMOSI/GPC6来控制
最终实现的效果:
1.和一般的232串口一样使用,应用层无需改变,占用的2个GPIO口就不能再做其它用了。
2.用户LED3,在COM0发送时亮(低电平),接收时灭(高电平),用户LED4,在COM2发送时亮(低电平),接收时灭(高电平)。
注意:
Mini6410的用户LED不知道是不是和Tiny6410的定义一样?Mini6410如果不一样可以自行更改,LED只是为了更好的看到COM0和COM2的通讯状态,如果不需要可以把LED控制部分的代码删除。
以下主要内容参考了网上一篇文章的源代码,想提供原文的链接但无奈搜索引擎上已经找不到了,现在搜到的都是一些提问贴或无用的不全的文章-_- 以下针对友善6410的内核版本:2.6.36,不同版本不保证能够成功。
需要修改内核源代码路径下的drivers/serial/samsung.c
1.因为要操作GPIO,所以要引用相关头文件#include <mach/gpio.h>
#include <plat/gpio-cfg.h>
2.把static struct uart_ops s3c24xx_serial_ops复制一份,并修改成如下,有3处需要修改为自定义的函数。static struct uart_ops s3c24xx_485_ops = {
.pm = s3c24xx_serial_pm,
.tx_empty = s3c24xx_serial_tx_empty,
.get_mctrl = s3c24xx_serial_get_mctrl,
.set_mctrl = s3c24xx_serial_set_mctrl,
.stop_tx = s3c24xx_485_stop_tx,
//这个 .start_tx = s3c24xx_485_start_tx,
//这个 .stop_rx = s3c24xx_serial_stop_rx,
.enable_ms = s3c24xx_serial_enable_ms,
.break_ctl = s3c24xx_serial_break_ctl,
.startup = s3c24xx_485_startup,
//这个 .shutdown = s3c24xx_serial_shutdown,
.set_termios = s3c24xx_serial_set_termios,
.type = s3c24xx_serial_type,
.release_port = s3c24xx_serial_release_port,
.request_port = s3c24xx_serial_request_port,
.config_port = s3c24xx_serial_config_port,
.verify_port = s3c24xx_serial_verify_port,
};
然后更改
static struct s3c24xx_uart_port s3c24xx_serial_ports[CONFIG_SERIAL_SAMSUNG_UARTS] = {
[0] = {
//这里是COM0 .port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[0].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX0,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_485_ops,
//指向刚才修改的ops .flags = UPF_BOOT_AUTOCONF,
.line = 0,
}
},
[1] = {//这里是COM1,不修改
.port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[1].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX1,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_serial_ops,
.flags = UPF_BOOT_AUTOCONF,
.line = 1,
}
},
#if CONFIG_SERIAL_SAMSUNG_UARTS > 2
[2] = {
//这里是COM2 .port = {
.lock = __SPIN_LOCK_UNLOCKED(s3c24xx_serial_ports[2].port.lock),
.iotype = UPIO_MEM,
.irq = IRQ_S3CUART_RX2,
.uartclk = 0,
.fifosize = 16,
.ops = &s3c24xx_485_ops,
//指向刚才修改的ops .flags = UPF_BOOT_AUTOCONF,
.line = 2,
}
},
3.定义s3c24xx_485_startupstatic int s3c24xx_485_startup(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
int ret;
#if defined(CONFIG_S5P_UART_DMA_EN)
unsigned int ucon;
#endif
//==========================================================
if (port->line == 0) { //COM0(GPC7)->LED3(GPK6)
s3c_gpio_cfgpin(S3C64XX_GPK(6), S3C_GPIO_OUTPUT);//GPK6/LED3配置成输出状态
gpio_set_value(S3C64XX_GPK(6),1);//GPK6/LED3默认灭
//----------------------------------------------------------
s3c_gpio_cfgpin(S3C64XX_GPC(7), S3C_GPIO_OUTPUT);//COM0(GPC7)配置成输出状态
gpio_set_value(S3C64XX_GPC(7), 0);//COM0(GPC7)默认0,常接收
}
if (port->line == 2) { //COM2(GPC6)->LED4(GPK7)
s3c_gpio_cfgpin(S3C64XX_GPK(7), S3C_GPIO_OUTPUT);//LED4(GPK7)配置成输出状态
gpio_set_value(S3C64XX_GPK(7),1);//LED4(GPK7)默认灭
//----------------------------------------------------------
s3c_gpio_cfgpin(S3C64XX_GPC(6), S3C_GPIO_OUTPUT);//COM2(GPC6)配置成输出状态
gpio_set_value(S3C64XX_GPC(6), 0);//COM2(GPC6)默认0,常接收
}
//==========================================================
dbg("s3c24xx_serial_startup: port=%p (%08lx,%p)\n",
port->mapbase, port->membase);
rx_enabled(port) = 1;
ret = request_irq(ourport->rx_irq, s3c24xx_serial_rx_chars, 0,
s3c24xx_serial_portname(port), ourport);
if (ret != 0) {
printk(KERN_ERR "cannot get irq %d\n", ourport->rx_irq);
return ret;
}
ourport->rx_claimed = 1;
dbg("requesting tx irq...\n");
tx_enabled(port) = 1;
ret = request_irq(ourport->tx_irq, s3c24xx_485_tx_chars, 0,
s3c24xx_serial_portname(port), ourport);
if (ret) {
printk(KERN_ERR "cannot get irq %d\n", ourport->tx_irq);
goto err;
}
ourport->tx_claimed = 1;
dbg("s3c24xx_serial_startup ok\n");
//===============================================================================================
#if defined(CONFIG_S5P_UART_DMA_EN)
if (s3c2410_dma_request(ourport->dma_ch_tx, &s5p_uart_dma_client, NULL)) {
printk(KERN_ERR "unable to get DMA channel for UART tx.\n" );
ourport->dma_ch_tx = 0;
goto err;
}
s3c2410_dma_set_buffdone_fn(ourport->dma_ch_tx, s5pc1xx_uart_tx_buffdone);
s3c2410_dma_set_opfn(ourport->dma_ch_tx, NULL);
s3c2410_dma_devconfig(ourport->dma_ch_tx, S3C2410_DMASRC_MEM, 0, (port->mapbase + S3C2410_UTXH) );
s3c2410_dma_config(ourport->dma_ch_tx, S3C_DMA_XFER_BYTE, 0);
s3c2410_dma_setflags(ourport->dma_ch_tx, S3C2410_DMAF_AUTOSTART);
//for rx
if (s3c2410_dma_request(ourport->dma_ch_rx, &s5p_uart_dma_client_rx, NULL)) {
printk(KERN_ERR "unable to get DMA channel for UART rx.\n" );
ourport->dma_ch_rx = 0;
goto err;
}
s3c2410_dma_set_buffdone_fn(ourport->dma_ch_rx, s5pc1xx_uart_rx_buffdone);
s3c2410_dma_set_opfn(ourport->dma_ch_rx, NULL);
s3c2410_dma_devconfig(ourport->dma_ch_rx, S3C2410_DMASRC_HW, 0, (port->mapbase + S3C2410_URXH) );
s3c2410_dma_config(ourport->dma_ch_rx, S3C_DMA_XFER_BYTE, 0);
s3c2410_dma_setflags(ourport->dma_ch_rx, S3C2410_DMAF_AUTOSTART);
ucon = rd_regl(port, S3C2410_UCON);
ucon &= ~(S3C2410_UCON_RXDMAMODE_MASK);
ucon |= S3C2410_UCON_RXDMAMODE_SIG1;
wr_regl(port, S3C2410_UCON, ucon);
ucon = rd_regl(port, S3C2410_UFCON);
ucon &= ~(3<<4);
wr_regl(port, S3C2410_UFCON, ucon);
ourport->rx_buf.buf = 0;
ourport->rx_buf.buf = (unsigned char *)dma_alloc_coherent(NULL, UART_RX_SIZE, &rx_buf_dma_handle, GFP_KERNEL);
ourport->rx_buf.head = 0;
ourport->rx_buf.tail = 0;
s3c2410_dma_enqueue(ourport->dma_ch_rx, (void *) ourport, (dma_addr_t) rx_buf_dma_handle, UART_RX_SIZE/2);
s3c2410_dma_enqueue(ourport->dma_ch_rx, (void *) ourport, (dma_addr_t) (rx_buf_dma_handle + (UART_RX_SIZE/2)) , UART_RX_SIZE/2);
init_timer( &(ourport->rx_dma_timer) );
ourport->rx_dma_timer.data = (unsigned long)(ourport);
ourport->rx_dma_timer.function = (void *)s5p_serial_rx_dma_timeout;
ourport->rx_dma_timer.expires = jiffies + DMA_RX_FLUSH_JIFFIES;
add_timer(&(ourport->rx_dma_timer));
//for rx
#endif
//===============================================================================================
/* the port reset code should have done the correct
* register setup for the port controls */
return ret;
err:
s3c24xx_serial_shutdown(port);
return ret;
}
4.定义s3c24xx_485_tx_chars,英文注释的内容是关键static irqreturn_t s3c24xx_485_tx_chars(int irq, void *id)
{
struct s3c24xx_uart_port *ourport = id;
struct uart_port *port = &ourport->port;
struct circ_buf *xmit = &port->state->xmit;
int count = 256;
//------------------------------------------------------
if (port->line == 0) { //COM0(GPC7)->LED3(GPK6)
gpio_set_value(S3C64XX_GPK(6),0);//发送前LED1灯亮
gpio_set_value(S3C64XX_GPC(7),1);//发送前改为发送状态
}
if (port->line == 2) { //COM2(GPC6)->LED4(GPK7)
gpio_set_value(S3C64XX_GPK(7),0);//发送前LED1灯亮
gpio_set_value(S3C64XX_GPC(6),1);//发送前改为发送状态
}
//------------------------------------------------------
if (port->x_char) {
wr_regb(port, S3C2410_UTXH, port->x_char);
port->icount.tx++;
port->x_char = 0;
goto out;
}
/* if there isnt anything more to transmit, or the uart is now
* stopped, disable the uart and exit
*/
if (uart_circ_empty(xmit) || uart_tx_stopped(port)) {
s3c24xx_485_stop_tx(port);
goto out;
}
/* try and drain the buffer... */
while (!uart_circ_empty(xmit) && count-- > 0) {
if (rd_regl(port, S3C2410_UFSTAT) & ourport->info->tx_fifofull)
break;
wr_regb(port, S3C2410_UTXH, xmit->buf[xmit->tail]);
xmit->tail = (xmit->tail + 1) & (UART_XMIT_SIZE - 1);
port->icount.tx++;
}
if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
uart_write_wakeup(port);
if (uart_circ_empty(xmit))
s3c24xx_485_stop_tx(port);
out:
return IRQ_HANDLED;
}
5.最后定义s3c24xx_485_start_tx和s3c24xx_485_stop_txstatic void s3c24xx_485_start_tx(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
//------------------------------------------------------
if (port->line == 0) { //COM0(GPC7)->LED3(GPK6)
gpio_set_value(S3C64XX_GPK(6),0);//发送前LED1灯亮
gpio_set_value(S3C64XX_GPC(7),1);//发送前改为发送状态
}
if (port->line == 2) { //COM2(GPC6)->LED4(GPK7)
gpio_set_value(S3C64XX_GPK(7),0);//发送前LED1灯亮
gpio_set_value(S3C64XX_GPC(6),1);//发送前改为发送状态
}
//------------------------------------------------------
#if defined(CONFIG_S5P_UART_DMA_EN)
if (!ourport->dma_busy) {
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_disable(port);
tx_enabled(port) = 1;
s5pc1xx_uart_enqueue(ourport);
}
#else
if (!tx_enabled(port)) {
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_disable(port);
enable_irq(ourport->tx_irq);
tx_enabled(port) = 1;
}
#endif
}
static void s3c24xx_485_stop_tx(struct uart_port *port)
{
struct s3c24xx_uart_port *ourport = to_ourport(port);
int iDelay = 0;
if (tx_enabled(port)) {
disable_irq_nosync(ourport->tx_irq);
tx_enabled(port) = 0;
if (port->flags & UPF_CONS_FLOW)
s3c24xx_serial_rx_enable(port);
}
while(!s3c24xx_serial_txempty_nofifo(port))//如果发送没完成,则延时
{
mdelay(3);
iDelay++;
if (iDelay > 16)
break;
}
//------------------------------------------------------
if (port->line == 0) { //COM0(GPC7)->LED3(GPK6)
gpio_set_value(S3C64XX_GPK(6),1);//发送完成后灭
gpio_set_value(S3C64XX_GPC(7),0);//发送完成后改为接收状态
}
if (port->line == 2) { //COM2(GPC6)->LED4(GPK7)
gpio_set_value(S3C64XX_GPK(7),1);//发送完成后灭
gpio_set_value(S3C64XX_GPC(6),0);//发送完成后改为接收状态
}
//------------------------------------------------------
}
6.最后编译内核,如果你已经编译过内核,则需要删除samsung.o再重新编译附件提供修改过的驱动文件以及没有修改过的原文件,仅供参考。
本部分内容设定了隐藏,需要回复后才能看到
最后,能找到的一些参考资料
https://groups.google.com/forum/?fromgroups=#!topic/linux-kernel-proxy/q2GrrKo_fikhttp://www.mcu123.com/news/article/interface/rs232/200607/15.html