1:检查AP端串口配置是否ok:
a:高通平台查看DMA传输:
echo 1 > /sys/kernel/debug/msm_serial_hsl/loopback.0 //打开回环开关
adb shell cat /dev/ttyHSL1
另起窗口
# adb shell
# echo 11111111 > /dev/ttyHSL1
若DMA通道ok,控制台会循环显示;
b:查看uart gpio是否ok:
tx高电平、rfr为低电平,rx,cts为输入;
如果tx为低电平,那么gpio肯定没有配置好,再次检查gpio配置问题;
如果以上2步都ok,那么UART应该ok了,再次检查:
adb shell cat /dev/ttyHSL1
将TX与RX短接;
另起窗口
# adb shell
# echo 11111111 > /dev/ttyHSL1
循环显示那么恭喜UART功能配置好了。
2:Termios参数配置:
影响通讯数据格式的关键
几个参数:
1:波特率-speed,通常
115200,最高4M;
2:奇偶校验-Parity,通
常为None;
3:数据位-Data,通常
8bit;
4:停止位-Stopbits,通
常1bit;
一般情况下默认为115200 8N1,也就是波特率115200,8bit数据位,无奇偶校验,1bit停止位。
为了使得AP可以与模块串口通讯,必须先了解模块的termios设置。
Ap端termios设置:
首先应用打开串口时会设置termios:
termios.c_cflag &= ~CSIZE; termios.c_cflag |= CS8; termios.c_cflag &= ~CSTOPB; termios.c_cflag &= ~PARENB; termios.c_cflag &= ~CBAUD; termios.c_cflag |= B3000000; termios.c_cflag |= CREAD | CLOCAL; termios.c_cflag |= CRTSCTS; /* turn on hardware flow control */ termios.c_iflag &= ~(IXOFF | IXON | IXANY); /* soft flow control */
驱动接口会根据termios参数来设置底层串口:
以8064 msm_seriel_hs.c为例说明:
uart接口封装:
static struct uart_ops msm_hs_ops = { .tx_empty = msm_hs_tx_empty, .set_mctrl = msm_hs_set_mctrl_locked, .get_mctrl = msm_hs_get_mctrl_locked, .stop_tx = msm_hs_stop_tx_locked, .start_tx = msm_hs_start_tx_locked, .stop_rx = msm_hs_stop_rx_locked, .enable_ms = msm_hs_enable_ms_locked, .break_ctl = msm_hs_break_ctl, .startup = msm_hs_startup, .shutdown = msm_hs_shutdown, .set_termios = msm_hs_set_termios, .type = msm_hs_type, .config_port = msm_hs_config_port, .release_port = msm_hs_release_port, .request_port = msm_hs_request_port, .flush_buffer = msm_hs_flush_buffer_locked, };
下面是底层设置函数,上层打开串口,参数未固定的话驱动走的是8N1,底层调试只使用echo或者cat /dev/ttyHSL0时
波特率一律是9600,模块不是9600时那么通讯肯定会失败,所以AP需要根据模块重新设定。
波特率。
//设置termios接口
/* * termios : new ktermios * oldtermios: old ktermios previous setting * * Configure the serial port */ static void msm_hs_set_termios(struct uart_port *uport, struct ktermios *termios, struct ktermios *oldtermios) { unsigned int bps; unsigned long data; unsigned long flags; int ret; unsigned int c_cflag = termios->c_cflag; struct msm_hs_port *msm_uport = UARTDM_TO_MSM(uport); mutex_lock(&msm_uport->clk_mutex); spin_lock_irqsave(&uport->lock, flags); /* * Disable Rx channel of UARTDM * DMA Rx Stall happens if enqueue and flush of Rx command happens * concurrently. Hence before changing the baud rate/protocol * configuration and sending flush command to ADM, disable the Rx * channel of UARTDM. * Note: should not reset the receiver here immediately as it is not * suggested to do disable/reset or reset/disable at the same time. */ data = msm_hs_read(uport, UARTDM_DMEN_ADDR); data &= ~UARTDM_RX_DM_EN_BMSK; msm_hs_write(uport, UARTDM_DMEN_ADDR, data); /* 300 is the minimum baud support by the driver */ bps = uart_get_baud_rate(uport, termios, oldtermios, 200, 4000000); /* Temporary remapping 200 BAUD to 3.2 mbps */ if (bps == 200) bps = 3200000; uport->uartclk = clk_get_rate(msm_uport->clk); if (!uport->uartclk) msm_hs_set_std_bps_locked(uport, bps); else flags = msm_hs_set_bps_locked(uport, bps, flags);//函数里面设置baud; data = msm_hs_read(uport, UARTDM_MR2_ADDR); data &= ~UARTDM_MR2_PARITY_MODE_BMSK; /* set parity */ //设置奇偶校验 if (PARENB == (c_cflag & PARENB)) { if (PARODD == (c_cflag & PARODD)) { data |= ODD_PARITY; } else if (CMSPAR == (c_cflag & CMSPAR)) { data |= SPACE_PARITY; } else { data |= EVEN_PARITY; } } /* Set bits per char */ //设置数据位 data &= ~UARTDM_MR2_BITS_PER_CHAR_BMSK; switch (c_cflag & CSIZE) { case CS5: data |= FIVE_BPC; break; case CS6: data |= SIX_BPC; break; case CS7: data |= SEVEN_BPC; break; default: data |= EIGHT_BPC; break; } /* stop bits */ //设置停止位 if (c_cflag & CSTOPB) { data |= STOP_BIT_TWO; } else { /* otherwise 1 stop bit */ data |= STOP_BIT_ONE; } data |= UARTDM_MR2_ERROR_MODE_BMSK; /* write parity/bits per char/stop bit configuration */ msm_hs_write(uport, UARTDM_MR2_ADDR, data); /* Configure HW flow control */ //设置是否使用硬件流控 data = msm_hs_read(uport, UARTDM_MR1_ADDR); data &= ~(UARTDM_MR1_CTS_CTL_BMSK | UARTDM_MR1_RX_RDY_CTL_BMSK); if (c_cflag & CRTSCTS) { data |= UARTDM_MR1_CTS_CTL_BMSK; data |= UARTDM_MR1_RX_RDY_CTL_BMSK; } msm_hs_write(uport, UARTDM_MR1_ADDR, data); uport->ignore_status_mask = termios->c_iflag & INPCK; uport->ignore_status_mask |= termios->c_iflag & IGNPAR; uport->ignore_status_mask |= termios->c_iflag & IGNBRK; uport->read_status_mask = (termios->c_cflag & CREAD); msm_hs_write(uport, UARTDM_IMR_ADDR, 0); /* Set Transmit software time out */ uart_update_timeout(uport, c_cflag, bps); msm_hs_write(uport, UARTDM_CR_ADDR, RESET_RX); msm_hs_write(uport, UARTDM_CR_ADDR, RESET_TX); if (msm_uport->rx.flush == FLUSH_NONE) { wake_lock(&msm_uport->rx.wake_lock); msm_uport->rx.flush = FLUSH_IGNORE; /* * Before using dmov APIs make sure that * previous writel are completed. Hence * dsb requires here. */ mb(); msm_uport->rx_discard_flush_issued = true; /* do discard flush */ msm_dmov_flush(msm_uport->dma_rx_channel, 0); spin_unlock_irqrestore(&uport->lock, flags); pr_debug("%s(): wainting for flush completion.\n", __func__); ret = wait_event_timeout(msm_uport->rx.wait, msm_uport->rx_discard_flush_issued == false, RX_FLUSH_COMPLETE_TIMEOUT); if (!ret) pr_err("%s(): Discard flush completion pending.\n", __func__); spin_lock_irqsave(&uport->lock, flags); } msm_hs_write(uport, UARTDM_IMR_ADDR, msm_uport->imr_reg); mb(); spin_unlock_irqrestore(&uport->lock, flags); mutex_unlock(&msm_uport->clk_mutex); }
3:串口通讯问题总结:
遇见过模块不能响应AP的问题:
1:硬件流控问题,若模块使用了硬件流控,配置rfr与cts,使得模块认为
AP准备好了,可以发送数据了。
2:AP端波特率没有与模块匹配上,要是怀疑AP波特率设置是否ok,可以将串口线连接TX,
设置termios 波特率来看输出是否ok,这种方法还可以测试验证AP端各个波特率是否OK,我使用的
minicon可以验证到460800。