赞
踩
Klipper 底层硬件的串口模块程序写的是否正确是决定下位机与上位机能否正常通信的前提,如果这个文件的驱动没写好,那上位机控制下位机就无从谈起,更无法通过上位机去验证下位机程序的正确性。
本篇博文将详细解析 Klipper src 文件夹下的 lpc176x 单片机的 serial.c,来分析其中的 API 函数的功能和作用,以此为新的平台的串口驱动的移植提供参考。
serial_init 函数主要是:
1. 对串口的根时钟进行初始化;
2. 设置通信配置:
代码及注释如下:
- void
- serial_init(void)
- {
- // Setup baud
- enable_pclock(PCLK_UARTx); //使能 UART 时钟
- LPC_UARTx->LCR = (1<<7); // set DLAB bit 开启对除数锁存器的访问
- uint32_t pclk = get_pclock_frequency(PCLK_UARTx); //得到时钟频率
- uint32_t div = pclk / (CONFIG_SERIAL_BAUD * 16); //的分频系数
- LPC_UARTx->DLL = div & 0xff; //设置分频系数低 8 位
- LPC_UARTx->DLM = (div >> 8) & 0xff; //设置分频系数高 8 位
- LPC_UARTx->FDR = 0x10; //取消预分频
- LPC_UARTx->LCR = 3; // 8N1 ; clear DLAB bit,设置停止位、奇偶校验、极性
-
- // Enable fifo
- LPC_UARTx->FCR = 0x01; //使能 FIFO
-
- // Setup pins
- gpio_peripheral(GPIO_Rx, GPIO_FUNCTION_UARTx, 0);
- gpio_peripheral(GPIO_Tx, GPIO_FUNCTION_UARTx, 0);
-
- // Enable receive irq
- armcm_enable_irq(UARTx_IRQHandler, UARTx_IRQn, 0);
- LPC_UARTx->IER = 0x01; //使能接收中断
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
程序使用 serial_enable_tx_irq API 函数来进行发送中断的使能:
- void
- serial_enable_tx_irq(void)
- {
- if (LPC_UARTx->LSR & (1<<5)) { //上一字节发送完成
- irqstatus_t flag = irq_save();
- kick_tx(); //发送一个字节后使能发送中断
- irq_restore(flag);
- }
- }
对于 kick_tx 函数,其中的逻辑是:
- // Write tx bytes to the serial port
- static void kick_tx(void)
- {
- for (;;) {
- if (!(LPC_UARTx->LSR & (1<<5))) {
- // Output fifo full - enable tx irq,有数据需要发送,使能发送中断
- LPC_UARTx->IER = 0x03;
- break;
- }
- uint8_t data;
- int ret = serial_get_tx_byte(&data);
- if (ret) {
- // No more data to send - disable tx irq
- LPC_UARTx->IER = 0x01;
- break;
- }
- LPC_UARTx->THR = data;
- }
- }
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
从 IER 寄存器可以看到 0x01 对应的是 Enable 接收数据中断,0x03 则同时 Enable 了接收和发送中断。
中断服务函数将通过 IIR 寄存器判断是否达到了中断条件,若产生了 THRE 中断,即获取到 status 为 0x02,即数据发送寄存器 THR 为空,程序将产生串口中断,在程序使能发送中断时,在 kick_tx 中发出一个 FIFO 的数据,THR 将为空,程序将发生中断发送下一个 FIFO 的数据,直到 Disable 掉串口发送中断。
- void UARTx_IRQHandler(void)
- {
- uint32_t iir = LPC_UARTx->IIR, status = iir & 0x0f; //获取中断状态
- if (status == 0x04) //接收中断
- serial_rx_byte(LPC_UARTx->RBR);
- else if (status == 0x02) //发送中断
- kick_tx();
- }
从 IIR 寄存器可以看到 0x02 对应的是发生了接收数据中断,0x04 是出现了发送中断。
程序只有安照正确的逻辑编写 API 函数,Klipper 下位机驱动才可以适配上层的应用层代码,对于 serial.c 程序,核心是理解中断发送和接收的条件,在应用代码的基础上编写发送和接收函数。
完整代码如下:
- // lpc176x serial port
- //
- // Copyright (C) 2018-2021 Kevin O'Connor <kevin@koconnor.net>
- //
- // This file may be distributed under the terms of the GNU GPLv3 license.
-
- #include "board/armcm_boot.h" // armcm_enable_irq
- #include "autoconf.h" // CONFIG_SERIAL_BAUD
- #include "board/irq.h" // irq_save
- #include "board/serial_irq.h" // serial_rx_data
- #include "command.h" // DECL_CONSTANT_STR
- #include "internal.h" // gpio_peripheral
- #include "sched.h" // DECL_INIT
-
- #if CONFIG_LPC_SERIAL_UART0_P03_P02
- DECL_CONSTANT_STR("RESERVE_PINS_serial", "P0.3,P0.2");
- #define GPIO_Rx GPIO(0, 3)
- #define GPIO_Tx GPIO(0, 2)
- #define GPIO_FUNCTION_UARTx 1
- #define LPC_UARTx LPC_UART0
- #define UARTx_IRQn UART0_IRQn
- #define PCLK_UARTx PCLK_UART0
- #elif CONFIG_LPC_SERIAL_UART3_P429_P428
- DECL_CONSTANT_STR("RESERVE_PINS_serial", "P4.29,P4.28");
- #define GPIO_Rx GPIO(4, 29)
- #define GPIO_Tx GPIO(4, 28)
- #define GPIO_FUNCTION_UARTx 3
- #define LPC_UARTx LPC_UART3
- #define UARTx_IRQn UART3_IRQn
- #define PCLK_UARTx PCLK_UART3
- #endif
-
- // Write tx bytes to the serial port
- static void
- kick_tx(void)
- {
- for (;;) {
- if (!(LPC_UARTx->LSR & (1<<5))) {
- // Output fifo full - enable tx irq,有数据需要发送,使能发送中断
- LPC_UARTx->IER = 0x03;
- break;
- }
- uint8_t data;
- int ret = serial_get_tx_byte(&data);
- if (ret) {
- // No more data to send - disable tx irq
- LPC_UARTx->IER = 0x01;
- break;
- }
- LPC_UARTx->THR = data;
- }
- }
-
- void
- UARTx_IRQHandler(void)
- {
- uint32_t iir = LPC_UARTx->IIR, status = iir & 0x0f; //获取中断状态
- if (status == 0x04) //接收中断
- serial_rx_byte(LPC_UARTx->RBR);
- else if (status == 0x02) //发送中断
- kick_tx();
- }
-
- void
- serial_enable_tx_irq(void)
- {
- if (LPC_UARTx->LSR & (1<<5)) { //上一字节发送完成
- irqstatus_t flag = irq_save();
- kick_tx(); //发送一个字节后使能发送中断
- irq_restore(flag);
- }
- }
-
- void
- serial_init(void)
- {
- // Setup baud
- enable_pclock(PCLK_UARTx); //使能 UART 时钟
- LPC_UARTx->LCR = (1<<7); // set DLAB bit 开启对除数锁存器的访问
- uint32_t pclk = get_pclock_frequency(PCLK_UARTx); //得到时钟频率
- uint32_t div = pclk / (CONFIG_SERIAL_BAUD * 16); //的分频系数
- LPC_UARTx->DLL = div & 0xff; //设置分频系数低 8 位
- LPC_UARTx->DLM = (div >> 8) & 0xff; //设置分频系数高 8 位
- LPC_UARTx->FDR = 0x10; //取消预分频
- LPC_UARTx->LCR = 3; // 8N1 ; clear DLAB bit,设置停止位、奇偶校验、极性
-
- // Enable fifo
- LPC_UARTx->FCR = 0x01; //使能 FIFO
-
- // Setup pins
- gpio_peripheral(GPIO_Rx, GPIO_FUNCTION_UARTx, 0);
- gpio_peripheral(GPIO_Tx, GPIO_FUNCTION_UARTx, 0);
-
- // Enable receive irq
- armcm_enable_irq(UARTx_IRQHandler, UARTx_IRQn, 0);
- LPC_UARTx->IER = 0x01; //使能接收中断
- }
- DECL_INIT(serial_init);
![](https://csdnimg.cn/release/blogv2/dist/pc/img/newCodeMoreWhite.png)
六. 参考文献
1. 来源:NXP 官网,UM10360
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。