ns16550串口驱动

之前学习的STM32F103的串口时,特点是:没有FIFO、参考手册写的很棒。

这里主要介绍一下a20的串口模块,之前把printf重定向分析了个大概,没有具体到ns16550的驱动,今天还是网上找了一些资料。发现a20貌似采用的是SISDA-16550-UART的IP参考手册地址:http://www.cs.indiana.edu/classes/b649-sjoh/reading/SISDA-16550-UART.pdf,参考手册的最后还给出了怎么与arm架构、AVR架构集成的例子,再次想起来前面分析的a20串口驱动是移植的ns16550串口驱动,a20很可能集成的就是SISDA-16550-UART。

所以这里就分析一下NS16550的驱动,分析过程中会与STM32F103的串口模块对比。

从大的方面说起,ns16550模块存在FIFO、MODEM,这些是stm32f103不存在的。与stm32共同的特点是中断、DMA。

再分析一下寄存器结构,a20参考手册


刚开始看的时候UART_RBR、UART_THR、UART_DLL的偏移地址竟然都是0x00,再看看代码中如何定义结构体

struct NS16550 {
	UART_REG(rbr);		/* 0 */
	UART_REG(ier);		/* 1 */
	UART_REG(fcr);		/* 2 */
	UART_REG(lcr);		/* 3 */
	UART_REG(mcr);		/* 4 */
	UART_REG(lsr);		/* 5 */
	UART_REG(msr);		/* 6 */
	UART_REG(spr);		/* 7 */
#ifdef CONFIG_SOC_DA8XX
	UART_REG(reg8);		/* 8 */
	UART_REG(reg9);		/* 9 */
	UART_REG(revid1);	/* A */
	UART_REG(revid2);	/* B */
	UART_REG(pwr_mgmt);	/* C */
	UART_REG(mdr1);		/* D */
#else
	UART_REG(mdr1);		/* 8 */
	UART_REG(reg9);		/* 9 */
	UART_REG(regA);		/* A */
	UART_REG(regB);		/* B */
	UART_REG(regC);		/* C */
	UART_REG(regD);		/* D */
	UART_REG(regE);		/* E */
	UART_REG(uasr);		/* F */
	UART_REG(scr);		/* 10*/
	UART_REG(ssr);		/* 11*/
	UART_REG(reg12);	/* 12*/
	UART_REG(osc_12m_sel);	/* 13*/
#endif
};
可以看出结构体根本没有对UART_THR、UART_DLL的定义,其实紧接着就是对偏移地址重复的寄存器定义

#define thr rbr
#define iir fcr
#define dll rbr
#define dlm ier
寄存器定义也找到了,那么问题来了,由于收发寄存器偏移地址一样,怎么读写呢?想写的时候它到底是发送寄存器还是接受寄存器?(没办法,新手想的就是这,还停留在STM32F103串口的经验中),看看参考手册吧


主要是对接收数据的说明和开启FIFO的介绍,刚开始看的时候,理解为:只有LCR的DR位被置1的时候,读RBR才有意义。所以想着直接设置LCR的DR位为1然后就能读RBR。这个想法是把UART_RBR、UART_THR、UART_DLL看做是BANK,就像内存BANK,通过高位来选择片选从而映射到不同内存BANK的地址。怎么验证呢?先看看手册吧,看了LCR寄存器的描述之后更郁闷了,压根没有DR位的定义。无语了,再来看看收发函数的代码吧

char NS16550_getc(NS16550_t com_port)
{
	while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0) {
#if !defined(CONFIG_SPL_BUILD) && defined(CONFIG_USB_TTY)
		extern void usbtty_poll(void);
		usbtty_poll();
#endif
		WATCHDOG_RESET();
	}
	return serial_in(&com_port->rbr);
}
我去,无语了,while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0)。怎么是LSR,再看看参考手册中LSR的描述,果然有DR位的描述,无语了,手册错误了。

OK继续,while ((serial_in(&com_port->lsr) & UART_LSR_DR) == 0)的意思是UART_LSR_DR为0就一直循环,而且循环体里根本没有对UART_LSR_DR位的操作,无语了,解释为由硬件逻辑自动置1吗?原来理解的直接设置LCR的DR位为1然后就能读RBR的想法是错误的。这里再次响起STM32F103的参考手册中的约定,有直接置1、硬件逻辑自动置1、通过写a位来置1b位(虽然写a位,单a位不存在是否置的意义)、通过写入数据序列来置1某位。这就是参考手册为什么会有版本号的原因。

继续吧,同理可以看一下THR寄存器的描述,跟RBR寄存器的描述差不多一样。再看看DLL的描述


这里说的LCR的DLAB位也是自动置1吗?还是看看代码吧(一般在串口的初始化中设置时钟,所以看初始化代码)

void NS16550_init(NS16550_t com_port, int baud_divisor)
{
#if (defined(CONFIG_SPL_BUILD) && defined(CONFIG_OMAP34XX))
	/*
	 * On some OMAP3 devices when UART3 is configured for boot mode before
	 * SPL starts only THRE bit is set. We have to empty the transmitter
	 * before initialization starts.
	 */
	if ((serial_in(&com_port->lsr) & (UART_LSR_TEMT | UART_LSR_THRE))
	     == UART_LSR_THRE) {
		serial_out(UART_LCR_DLAB, &com_port->lcr);
		serial_out(baud_divisor & 0xff, &com_port->dll);
		serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm);
		serial_out(UART_LCRVAL, &com_port->lcr);
		serial_out(0, &com_port->mdr1);
	}
#endif

	while (!(serial_in(&com_port->lsr) & UART_LSR_TEMT))
		;

	serial_out(CONFIG_SYS_NS16550_IER, &com_port->ier);
#if defined(CONFIG_OMAP) || defined(CONFIG_AM33XX) || \
			defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX)
	serial_out(0x7, &com_port->mdr1);	/* mode select reset TL16C750*/
#endif
	serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr);
	serial_out(0, &com_port->dll);
	serial_out(0, &com_port->dlm);
	serial_out(UART_LCRVAL, &com_port->lcr);
	serial_out(UART_MCRVAL, &com_port->mcr);
	serial_out(UART_FCRVAL, &com_port->fcr);
	serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr);
	serial_out(baud_divisor & 0xff, &com_port->dll);
	serial_out((baud_divisor >> 8) & 0xff, &com_port->dlm);
	serial_out(UART_LCRVAL, &com_port->lcr);
#if (defined(CONFIG_OMAP) && !defined(CONFIG_OMAP3_ZOOM2)) || \
	defined(CONFIG_AM33XX) || defined(CONFIG_SOC_DA8XX) || \
	defined(CONFIG_TI81XX) || defined(CONFIG_AM43XX)

	/* /16 is proper to hit 115200 with 48MHz */
	serial_out(0, &com_port->mdr1);
#endif /* CONFIG_OMAP */
}
其中与LCR有关的是serial_out(UART_LCR_BKSE | UART_LCRVAL, &com_port->lcr);其中#define UART_LCR_BKSE 0x80 /* Bank select enable */

很明白对LCR的DLAB位置1,但是为什么是UART_LCR_BKSE,而且#define UART_LCR_DLAB0x80 /* Divisor latch access bit *。想说既然UART_LCR_BKSE与UART_LCR_DLAB相等,而且此处的意思好像就是设置DLAB位(因为下面有两句serial_out(0, &com_port->dll);serial_out(0, &com_port->dlm);很符合参考手册上说的:DLAB位置1之后才能设置DLL)。总结一下就是LCR的DLAB位要软件置1。


上面是uart寄存器结构体的理解。再来看看ns16550.c文件吧,一共5个函数。

void NS16550_init(NS16550_t com_port, int baud_divisor)

void NS16550_reinit(NS16550_t com_port, int baud_divisor)

void NS16550_putc(NS16550_t com_port, char c)

char NS16550_getc(NS16550_t com_port)

int NS16550_tstc(NS16550_t com_port)

虽然不都是叶子函数,但是其调用的子函数WATCHDOG_RESET();也难分析,有了刚才对结构体的分析,感觉这几个函数就不用说了。

只想说说刚才的UART_LCR_DLAB与UART_LCR_BKSE,整个项目中两处很值得注意

<arch/arm/armv7/sunxi/early_print.c>中

void uart_init(void) {

	/* select dll dlh */
	writel(UART_LCR_DLAB, UART_LCR(UART));
	/* set baudrate */
	writel(0, UART_DLH(UART));
	writel(BAUD_115200, UART_DLL(UART));
	/* set line control */
	writel(LC_8_N_1, UART_LCR(UART));

	uart_initialized = 1;
}

还有void NS16550_init(NS16550_t com_port, int baud_divisor)中,在这两个函数中UART_LCR_DLAB都是为了设置时钟,虽然这两处都不会被编译。

最后还要说一下不要忘了uart的时钟与GPIO配置,这两部分已经在前面分析过的s_init中完成。

<think>好的,我现在需要介绍一下NS32F103C8T6这款芯片。首先,我要确认一下型号的正确性,因为通常市面上常见的应该是STM32F103C8T6,这是STMicroelectronics的经典产品。用户提到的NS32F103C8T6可能是国内厂商的兼容型号,或者是用户输入时的笔误。这种情况下,我需要先核实是否存在这个型号,或者是否是STM32的替代产品。 假设NS32F103C8T6确实是某个厂商生产的兼容STM32F103C8T6的芯片,那么它的基本参数应该和STM32的类似。接下来需要介绍它的核心架构,比如基于ARM Cortex-M3,主频72MHz,Flash和RAM的大小,比如64KB Flash,20KB RAM,外设包括ADC、定时器、通信接口等。同时,要提到它的封装形式,比如LQFP48,适用领域如工业控制、消费电子等。 另外,作为兼容芯片,可能需要强调其与STM32的兼容性,比如是否引脚兼容、软件是否可以直接移植,或者是否需要特别注意的地方,比如某些外设的差异,供电电压的不同等。还可能提到价格优势,或者供货情况,这对于开发者来说也是重要的信息。 需要注意的是,如果NS32F103C8T6是某个特定厂商的产品,可能需要查阅该厂商的官方资料来确认具体参数,避免错误。如果找不到相关资料,应该说明可能存在型号错误,并建议用户确认型号或提供更多信息。 最后,总结这款芯片的特点,适用场景,以及开发时可能用到的工具和资源,比如Keil、IAR、Arduino支持,或者使用STM32的库进行开发等。</think>**NS32F103C8T6** 是一款基于 **ARM Cortex-M3 内核**的 32 位微控制器(MCU),通常被认为是 **STM32F103C8T6**(意法半导体经典型号)的国产兼容替代品。以下是其核心特点与典型应用场景: --- ### **关键参数** 1. **内核与主频** - ARM Cortex-M3 架构,最高主频 **72MHz**,性能适用于实时控制场景。 2. **存储配置** - **64KB Flash**(程序存储) + **20KB SRAM**(运行内存),满足中等复杂度嵌入式应用。 3. **外设资源** - **2x SPI**、**2x I²C**、**3x USART**(串口通信) - **2x ADC**(12位精度,支持 10 通道模拟信号采集) - **4x 通用定时器** + **1x PWM 高级定时器** - 支持 **USB 2.0** 全速接口(需注意部分兼容型号可能未开放此功能)。 4. **封装与功耗** - **LQFP48 封装**(7x7mm),紧凑易焊接。 - 工作电压:**2.0V–3.6V**,低功耗设计。 --- ### **优势与兼容性** - **硬件兼容性**:引脚与 STM32F103C8T6 高度兼容,可直接替换(需验证具体型号)。 - **软件生态**:支持 **Keil、IAR、Arduino** 等开发环境,可复用 STM32 标准库(如 HAL 库或 LL 库),降低迁移成本。 - **成本优势**:国产化方案通常价格更低,供货稳定性较好。 --- ### **典型应用场景** - **工业控制**:PLC、电机驱动、传感器数据处理。 - **消费电子**:智能家居、无人机、小型手持设备。 - **通信模块**:CAN/USB 转接器、蓝牙/WiFi 控制板。 - **教育开发**:高校实验、创客项目(如替代 STM32 最小系统板)。 --- ### **开发注意事项** 1. **文档确认**:建议查阅厂商手册,确认外设(如 USB、CAN)是否完全兼容。 2. **烧录工具**:支持 J-Link、ST-Link 或国产调试器(需配置为 Cortex-M3 内核)。 3. **性能验证**:部分国产型号极限主频或 ADC 精度可能与原厂存在差异,需实测验证。 如需进一步技术细节,建议提供具体厂商资料或确认型号准确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值