gpio模拟uart总线驱动

   在有众多的uart232,485以及422通信需求类的产品中,时常会出现主控板uart接口不够用的情况,而有些外设由于其特殊性,又必须单独占有一路uart的时候,我们能够做的就是使用众多的gpio去模拟uart进行数据通信。

linux下的uart模拟需要完成和涉及到一下几方面的内容:

1、gpio的选择和初始化

2、高精度内核定时器提供标准的波特率对应的时序控制。

3、接收引脚的中断控制以及数据处理信号反馈

4、增加内核fifo机制,用于缓存接受数据

5、select控制机制,将接收事件及时的反馈到应用层。

具体如下:

一、gpio的初始化

按部就班,按照所使用的主控芯片对应的linux内核api去做就ok,例如:

#define UART2_TX1                   GPIO_TO_PIN(0,12)
#define UART2_TX1_OUT        		gpio_direction_output(UART2_TX1, 1)
#define UART2_TX1_IN         		gpio_direction_input(UART2_TX1)
#define UART2_TX1_GET_VALUE   		gpio_get_value(UART2_TX1)
#define UART2_TX1_L  		   		gpio_set_value(UART2_TX1, 0)
#define UART2_TX1_H  		   		gpio_set_value(UART2_TX1, 1)

#define UART2_RX1                   GPIO_TO_PIN(0,13)
#define UART2_RX1_OUT        		gpio_direction_output(UART2_RX1, 1)
#define UART2_RX1_IN         		gpio_direction_input(UART2_RX1)
#define UART2_RX1_GET_VALUE   		gpio_get_value(UART2_RX1)
#define UART2_RX1_L  		   		gpio_set_value(UART2_RX1,0)
#define UART2_RX1_H  		   		gpio_set_value(UART2_RX1,1)

二、高精度内核定时器提供标准的波特率对应的时序控制

内核提供的api以及具体使用如下:

void tx_timer_init(void)
{
	hrtimer_init(&hr_timer_tx, CLOCK_MONOTONIC, HRTIMER_MODE_REL_PINNED);
    hr_timer_tx.function = tx_vibrator_timer_func;
}

void tx_timer_start(void)
{
	nsecs = (unsigned long)((1000000000/g_gpio_uart_config.baudrate));
	if(!tx_timer_start_flag)
	{
		hrtimer_start(&hr_timer_tx,ktime_set(0,nsecs),HRTIMER_MODE_REL_PINNED);
		tx_timer_start_flag = true;
	}
}

void tx_timer_stop(void)
{
	unsigned char ret = 0;
	if(tx_timer_start_flag)
	{
	 	ret = hrtimer_cancel( &hr_timer_tx );
	 	tx_timer_start_flag = false;
	}
    
}

void rx_timer_start(void)
{
	nsecs = (unsigned long)((1000000000/g_gpio_uart_config.baudrate));
	if(!rx_timer_start_flag)
	{
		hrtimer_start(&hr_timer_rx,ktime_set(0,nsecs/6),HRTIMER_MODE_REL_PINNED);
		rx_timer_start_flag = true;
	}
}

void rx_timer_stop(void)
{
	unsigned char ret = 0;
	if(rx_timer_start_flag)
	{
		ret = hrtimer_cancel( &hr_timer_rx ); 
		//if (ret)   
        //printk("hr_timer_rx was still in use...\n"); 
		rx_timer_start_flag = false;
	}
     
}

三、接收引脚的中断控制以及数据处理信号反馈

void set_rx_pin_interrupt_mode(void)
{
	unsigned char  retval = 0;
	irq_num = gpio_to_irq(UART2_RX1);
	retval = request_irq(irq_num, UART2_RX1_interrupt_handler, 0, "irq_uartrx", NULL);
	if (retval) {
		printk("Error requesting IRQ\n");
	}
	irq_set_irq_type(irq_num, IRQ_TYPE_EDGE_FALLING);
	disable_irq(irq_num);
	rx_irq_en = false;
}
void rx_pin_interrupt_free(void)
{
	free_irq(irq_num, NULL);
	irq_num = -1;
}

void set_rx_pin_interrupt_disable(void)
{
	if(rx_irq_en)
	{
		disable_irq_nosync(irq_num);
		rx_irq_en = false;
	}
	//irq_set_irq_wake(irq_num, 0);
}

void set_rx_pin_interrupt_enable(void)
{
	if(!rx_irq_en)
	{
		enable_irq(irq_num);
		rx_irq_en = true;
	}
	//irq_set_irq_wake(irq_num, 1);
}

四、增加内核fifo机制,用于缓存接受数据

这里为了便于数据的控制,没有使用内核提供的fifo机制,使用了自己编写的fifo处理函数

bool fifo_init(app_fifo_t * p_fifo, uint8_t * p_buf, uint16_t buf_size)
bool fifo_put(app_fifo_t * p_fifo, uint8_t byte)
bool fifo_get(app_fifo_t * p_fifo, uint8_t * p_byte)
bool fifo_peek(app_fifo_t * p_fifo, uint16_t index, uint8_t * p_byte)
bool fifo_flush(app_fifo_t * p_fifo)
bool fifo_read(app_fifo_t * p_fifo, uint8_t * p_byte_array, uint32_t * p_size)
bool fifo_write(app_fifo_t * p_fifo, uint8_t const * p_byte_array, uint32_t * p_size)

五、select控制机制,将接收事件及时的反馈到应用层。

比较简单也就是实现一个poll函数调用

static unsigned int tty_drv_poll(struct file *file, struct poll_table_struct *wait)
{
    unsigned int mask = 0;
    set_rx_pin_interrupt_enable();
     poll_wait(file, &tty_uart_waitq, wait);
    if(ev_read_world)
    {
        mask = POLLIN | POLLRDNORM
    }
    //printk("tty_drv_poll end ev_read_world = %d,mask = %d!\n",ev_read_world,mask);
    return mask;
}

接下来的工作就是将上边提到的5大块内容装到linux字符设备通用框架里,生成属于自己的gpio模拟uart/ttl驱动,就完成了具体的驱动功能实现。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章