F1C200S TX Empty trigger 设置成1/2触发要怎么做?
-
我想把发送FIFO设置成1/2空就触发中断,但一直修改不了。我估计是THRE_MODE_USER没有启用,但在用户手册上没有找到它的使用说明。
请问要怎么做才能修改发送FIFO的中断触发模式呢?
-
帮帮孩子吧。我在串口写FCR,在terminal用devmem写FCR,都写不动FIFO的TX Empty Trigger。是不是f1c100s、f1c200s根本就不让修改它?
-
-
@alb702 感谢回复!我在实现一个功能,控制485的in/out转换的gpio脚在合适的时候处于它应该在的电平:发送时拉高,接受时拉低。
现在遇到的问题是:通过示波器发现,发送数据时控制引脚提前转换电平,导致数据没有完全发完。
我想知道有没有什么好方法知道串口的数据在物理上什么时候实际地发送完成,然后尽量在1MS内的转换控制引脚的电平 -
即使是在接受到THEM(tx FIFOS holding register empty)中断之后才改变控制引脚的电平,仍然是在处于数据未发送完全的情形下就把控制引脚提前改变,导致数据传输不完全
-
@smbzd
监控FIFO的状态是控制485切换的错误方法。FIFO是一个中间存储器,这不知道发射器在做什么。 当最后一个字节的最后一个停止位离开发送器移位寄存器时,必须切换 485。
最正确的方法是控制器是否有 TX_COMPLETE 中断。 485 的控制必须在发送最后一个字节时在中断中完成。
但有些系统没有TX_COMPLETE中断,而只有TX_EMPTY中断。 在这种情况下,需要多传输1个字节,并在TX_EMPTY中断中切换控制485。这不是很正确,但它有效。在我们的项目中,我们使用专门的串口驱动程序,它也控制 485。
该驱动程序可用于多种系统,包括 F1C200s。 如果你有兴趣,我可以贴出原文的片段 驱动程序文本在这里。 -
@alb702 感谢您一直以来的帮助。关于驱动的片段,我十分感兴趣,如果能提供就真是帮大忙了。非常感谢您的慷慨
-
@smbzd
驱动程序算法的解释。驱动程序开发很久以前就开始了,在 Allwinner A20 的 legacy kernel 版本 3.xx 时期。 为了不使驱动程序适应Linux内核的不同API,采用直接写入GPIO和UART寄存器的方式。
在我们的项目中,内核驱动程序和用户应用程序之间的数据交换是通过缓冲区进行的。 mmap 用于从用户空间访问缓冲区。 缓冲区包含用于接收和发送的数据以及控制结构。 为了开始传输,应用程序在缓冲区中设置一个特殊位。 驱动程序在 bum_poll() 函数的定时器中断中扫描该位(它位于不同的源代码中)。
您很可能会使用其他方法:read()/write()、ioctl()...,并且您不需要计时器。/* * suniv xbus driver * (timer+uart) * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. * * You should have received a copy of the GNU General Public License * along with this programOC; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <linux/module.h> #include <linux/kernel.h> #include <linux/moduleparam.h> #include <asm/uaccess.h> #include <asm/pgtable.h> #include <linux/fs.h> #include <linux/gfp.h> #include <linux/cdev.h> #include <linux/sched.h> #include <linux/interrupt.h> #include <linux/of.h> #include <linux/of_irq.h> #include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/delay.h> #include <linux/ioport.h> #include <linux/slab.h> #include <linux/io.h> #include <linux/clk.h> #include <linux/reset.h> #include <linux/mm.h> #include <linux/miscdevice.h> #include <linux/wait.h> #include <linux/poll.h> static int hw_init; static int bum_init(void); static void bum_poll(void); static void gpio_set(int port, int pin); static void gpio_clr(int port, int pin); static enum hrtimer_restart xbus_periodic(struct hrtimer *); static struct hrtimer htimer; static int k_period; static uint32_t norecv_timer; static int htimer_init(void) { k_period = ktime_set(0, 1000000); //1 ms hrtimer_init(&htimer, CLOCK_REALTIME, HRTIMER_MODE_REL); htimer.function = xbus_periodic; hrtimer_start(&htimer, k_period, HRTIMER_MODE_REL); return 0; } static void htimer_exit(void) { hrtimer_cancel(&htimer); } static void stop_send(void); static uint8_t * xbus_iobuf = 0; #define xbus_mmap_mem() (xbus_iobuf) /* -----------------------------Hardware registers mini description --------------------*/ /*GPIO--------------------------------------*/ typedef struct { uint32_t CFG[4]; uint32_t DAT; uint32_t DRV[2]; uint32_t PULL[2]; } port_struct_t; enum {PORTA, PORTB, PORTC, PORTD, PORTE, PORTF, PORTG, PORTH,PORTI}; #define PORT_IO_BASE 0x01C20800 #define UART_IO_BASE 0x01C25000 static volatile __u8 * port_io_base; static volatile __u8 * uart_io_base; #define __gpio ((volatile port_struct_t *)(port_io_base)) /*UART---------------------------------------*/ #define UART_BASE (uart_io_base) #define UART_BASE_OS (0x400) #define UARTx_BASE(x) (UART_BASE + (x) * UART_BASE_OS) #define RESSIZE(res) (((res)->end - (res)->start)+1) //#define UART_HALT 0x29 /* Halt TX register */ #define UART_FORCE_CFG (1 << 1) #define UART_FORCE_UPDATE (1 << 2) #define __uart_reg(num, off) (*(volatile __u32 *)((UARTx_BASE(num)) + (off))) #define UART_RBR(n) __uart_reg(n, 0x00) // DLAB=0 #define UART_THR(n) __uart_reg(n, 0x00) // DLAB=0 #define UART_DLL(n) __uart_reg(n, 0x00) // DLAB=1 #define UART_DLH(n) __uart_reg(n, 0x04) // DLAB=1 #define UART_IER(n) __uart_reg(n, 0x04) #define UART_IIR(n) __uart_reg(n, 0x08) #define UART_FCR(n) __uart_reg(n, 0x08) #define UART_LCR(n) __uart_reg(n, 0x0c) #define UART_MCR(n) __uart_reg(n, 0x10) #define UART_LSR(n) __uart_reg(n, 0x14) #define UART_MSR(n) __uart_reg(n, 0x18) #define UART_SCH(n) __uart_reg(n, 0x1c) #define UART_USR(n) __uart_reg(n, 0x7c) #define UART_TFL(n) __uart_reg(n, 0x80) #define UART_RFL(n) __uart_reg(n, 0x84) #define UART_HALT(n) __uart_reg(n, 0xa4) /*APB1---------------------------------------------*/ #define __ccu_reg(off) (*(volatile __u32 *)((ccu_io_base) + (off))) #define APB1_GATING_REG __ccu_reg(0x6c) /*--------------------------------------------------------------------------*/ #define DTR_PORT PORTE #define DTR_PIN 6 //#define TIMERn 2 //#define IRQn_TIM 19//52 #define UARTn 2 #define UART_BAUDRATE 19200 #define IRQn_UART 33 #define CDEV_MAJOR 230 #define CDEV_NAME "b*m" //========================== GPIO functions====================== #define FMUX_IN 0 #define FMUX_OUT 1 #define FMUX_UART 3 static void gpio_init_pin(int port, int pin, int mux) { uint32_t tmp; int n = 0; while( pin > 7) { pin -= 8; n++; } tmp = __gpio[port].CFG[n] & ~(0xf << (pin << 2)); __gpio[port].CFG[n] = tmp | (mux << (pin << 2)); } static spinlock_t gpio_lock; static void gpio_set(int port, int pin) { spin_lock(&gpio_lock); __gpio[port].DAT |= (1 << pin); spin_unlock(&gpio_lock); } static void gpio_clr(int port, int pin) { spin_lock(&gpio_lock); __gpio[port].DAT &= ~(1 << pin); spin_unlock(&gpio_lock); } // ========================= UART functions ======================= inline static void start_send(void) { gpio_set(DTR_PORT, DTR_PIN); UART_IER(UARTn) |= (1 << 1); // set ETBEI (transmit holding register interrupt) } inline static void stop_send(void) { UART_IER(UARTn) &= ~(1 << 1); // clr ETBEI gpio_clr(DTR_PORT, DTR_PIN); } static enum hrtimer_restart xbus_periodic(struct hrtimer * unused) { if(hw_init) { norecv_timer++; if (norecv_timer > 2000) // 4s { gpio_clr(DTR_PORT, DTR_PIN); norecv_timer = 0; } bum_poll(); } hrtimer_forward_now(&htimer, k_period); return HRTIMER_RESTART; } //================================================================= #include "b*m-core.c" static char tdata[128]; static int idx; static irqreturn_t irq_serial_handle(int irq, void *dev_id) { /*UART interrupt handler*/ /* See "A20 User Manual 2013-03-22.pdf" , pp. 109 ... 122 */ int data; uint32_t tmp = UART_IIR(UARTn) & 0xf; if (tmp == 2) // THR empty { data = bum_uart_isr(-1); if (data == -1) stop_send(); else UART_THR(UARTn) = data; } else if (tmp == 4) // receiver data available { norecv_timer = 0; bum_uart_isr(UART_RBR(UARTn)); } return IRQ_HANDLED; } static int divisor; static int serial_hardware_init(void) { gpio_init_pin(DTR_PORT, DTR_PIN, FMUX_OUT); /* configure UART registers*/ /* See "A20 User Manual 2013-03-22.pdf" , pp. 645 ... 665 */ UART_HALT(UARTn) |= 1; UART_LCR(UARTn) |= (1 << 7); // set DLAB (LCR.7) UART_DLH(UARTn) = (divisor >> 8); UART_DLL(UARTn) = (divisor & 0xff); UART_FCR(UARTn) = 0;// disable FIFO UART_MCR(UARTn) = 0;// disable modem control UART_LCR(UARTn) = (3 << 0) // DLS, 8 bits word width | (0 << 2) // STOP , 1 stop bit | (0 << 3) // PEN, parity = disabled | (0 << 4) // EPS parity select, | (0 << 6) // BC, no break control | (0 << 7) // clear DLAB ; UART_IER(UARTn) = (1 << 0);// set ERBFI / Enable Receiver Data available interrupt UART_HALT(UARTn) = 0; //clear HALT return 0; } inline static void serial_hardware_stop(int irq, void * id) { free_irq(irq, id); } /* ==================== mmap implementation ======================*/ struct mmap_info { char * data; int reference; }; static void mmap_open(struct vm_area_struct *vma) { // struct mmap_info * info = (struct mmap_info *)vma->vm_private_data; // info->reference++; } static void mmap_close(struct vm_area_struct *vma) { // struct mmap_info * info = (struct mmap_info *)vma->vm_private_data; // info->reference--; } static unsigned int mmap_fault(struct vm_fault * vmf) { struct page * page; struct vm_area_struct *vma = vmf->vma; unsigned long size = (unsigned long)(vma->vm_end-vma->vm_start); // struct mmap_info * info = (struct mmap_info *)vma->vm_private_data; size = (unsigned long)(vma->vm_end-vma->vm_start); if (size > PAGE_SIZE) return -EINVAL; page = virt_to_page(xbus_iobuf); get_page(page); vmf->page = page; return 0; } static const struct vm_operations_struct vm_ops = { .open = mmap_open, .close = mmap_close, .fault = mmap_fault }; /*=========================== -- mmap =================================*/ static int xbus_mmap_init(void) { xbus_iobuf = (uint8_t*)get_zeroed_page(GFP_KERNEL); if (!xbus_iobuf) { printk(KERN_ERR "mmap init failed\n"); return 0; } return 0; } static void xbus_mmap_cleanup(void) { if (xbus_iobuf) free_page((unsigned long)xbus_iobuf); xbus_iobuf = 0; } #ifndef VM_RESERVED #define VM_RESERVED (VM_DONTEXPAND | VM_DONTDUMP) #endif static int mmap_xbus(struct file *filp, struct vm_area_struct *vma) { vma->vm_ops = &vm_ops; vma->vm_flags |= VM_RESERVED; vma->vm_private_data = filp->private_data; return 0; } ////////////////////////////////////////////////////////////////////////////// static DECLARE_WAIT_QUEUE_HEAD(wait); static int check_data_ready(void); static void indicate_data_ready(void) { wake_up_interruptible(&wait); } static unsigned int poll_xbus(struct file *f, struct poll_table_struct *p) { unsigned int mask = 0; poll_wait(f, &wait, p); if(check_data_ready()) mask |= POLLIN | POLLRDNORM; return mask; } // VFS methods: static struct file_operations xbus_fops = { .owner = THIS_MODULE, .mmap = mmap_xbus, .poll = poll_xbus, }; static struct miscdevice miscdev = { MISC_DYNAMIC_MINOR, "xbus", &xbus_fops }; static const struct of_device_id xbus_of_match[] = { { .compatible = "xbus-uart" }, { /* Sentinel */ } }; MODULE_DEVICE_TABLE(of, xbus_of_match); struct xbus_serial_data { int irq; struct clk *clk; struct clk *pclk; struct reset_control *rst; }; static struct xbus_serial_data xbus_serial; static void serial_remove( struct platform_device * pdev) { struct xbus_serial_data *data = platform_get_drvdata(pdev); reset_control_assert(data->rst); if (!IS_ERR(data->pclk)) clk_disable_unprepare(data->pclk); if (!IS_ERR(data->clk)) clk_disable_unprepare(data->clk); } static int serial_setup( struct platform_device * pdev) { struct resource *regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); int irq = platform_get_irq(pdev, 0); struct device *dev = &pdev->dev; struct xbus_serial_data *data; int err = -1; int uart_clk; if (!regs) { dev_err(dev, "no registers defined\n"); return -EINVAL; } if (irq < 0) { if (irq != -EPROBE_DEFER) dev_err(dev, "cannot get irq\n"); return irq; } data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) return -ENOMEM; data->clk = devm_clk_get(dev, "baudclk"); if (IS_ERR(data->clk) && PTR_ERR(data->clk) != -EPROBE_DEFER) data->clk = devm_clk_get(dev, NULL); if (IS_ERR(data->clk) && PTR_ERR(data->clk) == -EPROBE_DEFER) return -EPROBE_DEFER; if (!IS_ERR_OR_NULL(data->clk)) { err = clk_prepare_enable(data->clk); if (err) dev_warn(dev, "could not enable optional baudclk: %d\n", err); } /* If no clock rate is defined, fail. */ if (err) { dev_err(dev, "clock rate not defined\n"); return -EINVAL; } data->pclk = devm_clk_get(dev, "apb_pclk"); if (IS_ERR(data->pclk) && PTR_ERR(data->pclk) == -EPROBE_DEFER) { err = -EPROBE_DEFER; goto err_clk; } if (!IS_ERR(data->pclk)) { err = clk_prepare_enable(data->pclk); if (err) { dev_err(dev, "could not enable apb_pclk\n"); goto err_clk; } } uart_clk = clk_get_rate(data->clk); divisor = DIV_ROUND_CLOSEST(uart_clk, 16 * UART_BAUDRATE); data->rst = devm_reset_control_get_optional(dev, NULL); if (IS_ERR(data->rst)) { err = PTR_ERR(data->rst); goto err_pclk; } reset_control_deassert(data->rst); platform_set_drvdata(pdev, data); return irq; //err_reset: reset_control_assert(data->rst); err_pclk: if (!IS_ERR(data->pclk)) clk_disable_unprepare(data->pclk); err_clk: if (!IS_ERR(data->clk)) clk_disable_unprepare(data->clk); return err; } static int xbus_probe(struct platform_device *pdev) { int irq; printk(KERN_ERR "xbus loading..\n"); hw_init = 0; int err = misc_register(&miscdev); if(err != 0) { printk("misc_register() error :%d\n", err); return -EPROBE_DEFER; } irq = serial_setup(pdev); if (irq < 0) { return -EPROBE_DEFER; } xbus_serial.irq = irq; if (request_irq(irq, irq_serial_handle, IRQF_SHARED, "xbus-suniv", &xbus_serial)) { printk (KERN_ERR "request irq %d error.\n", irq); return -EPROBE_DEFER; } port_io_base = (__u8 * )ioremap(PORT_IO_BASE, 0x400); uart_io_base = (__u8 * )ioremap(UART_IO_BASE, 0x400); if (uart_io_base == 0 || port_io_base == 0 ) { printk(KERN_ERR "resource ioremap failed\n"); return -EPROBE_DEFER; } if (htimer_init() < 0) { iounmap(port_io_base); iounmap(uart_io_base); return -EPROBE_DEFER; } serial_hardware_init(); xbus_mmap_init(); bum_init(); hw_init = 1; return 0; } static int xbus_remove(struct platform_device *pdev) { printk(KERN_ERR "xbus unloading..\n"); hw_init = 0; htimer_exit(); serial_hardware_stop(xbus_serial.irq, &xbus_serial); serial_remove(pdev); iounmap(port_io_base); iounmap(uart_io_base); xbus_mmap_cleanup(); misc_deregister(&miscdev); return 0; } static struct platform_driver xbus_platform_driver = { .driver = { .name = "xbus-suniv", .owner = THIS_MODULE, .of_match_table = xbus_of_match, }, .probe = xbus_probe, .remove = xbus_remove, }; module_platform_driver(xbus_platform_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("albert@sokrat.ru"); MODULE_AUTHOR("jean@sokrat.ru");
bum_uart_isr() 它用于接收和传输。基本算法是:
static uint8_t tx_buf[MAX_TX]; static uint8_t rx_buf[MAX_RX]; static int rx_count; static int tx_count; static int snd_size; int bum_uart_isr(int value) { if (value < 0) // TX IRQ { if (tx_count < snd_size && tx_count < MAX_TX) value = (int)tx_buf[tx_count++]; } else // RX IRQ { if (rx_count < MAX_RX) rx_buf[rx_count++] = (uint8_t)value; } return value; }
-
@alb702 感谢您提供的编码,真的。祝福您。
Copyright © 2024 深圳全志在线有限公司 粤ICP备2021084185号 粤公网安备44030502007680号