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驅動,就完成了具體的驅動功能實現。

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