UART也是一种常用的总线,可以实现异步操作,速率比i2c要块。
1.标准是4线连接,分别是VCC、GND、RX、TX,但是根据不同的场景及用途,可以有3线和2线连接。
2.协议
UART使用的是异步串行通信协议,一步通信以一个字符为传输单位,通信中两个字符之间的时间间隔多少是不固定的,然而在同一个字符中的两个相邻位间的间隔是固定的。
数据传输速率用波特率来表示,即每秒传送二进制的位数,例如:数据传送速率为120字符/秒,而每一个字符10位(1个起始位,7个数据位,一个校验位,一个结束位),则其传送的波特率为10x120=1200位/秒。
数据传输格式如下所示:
(1)起始位,先发一个逻辑“0”,表示开始传输
(2)5-8位逻辑“0”或“1”,如ASCII码7位,扩展BCD码8位,并且是小端传输模式
(3)校验位,数据位加上这一位后,使得“1“的个数为偶数,表示偶校验;”1“的个数为奇数,表示奇校验
(4)停止位,一个字符数据传输结束的标志,可以是1位,1.5位,2位的高电平
(5)空闲状态,高电平,表示当前线路没有数据传输
基本了解后,就是UART在lk中的配置了。
首先将debug的宏打开
路径:bootable/bootloader/lk/project/msm8916.mk
DEFINES += WITH_DEBUG_UART = 1
接下来是UART的初始化了
路径:bootable/bootloader/lk/target/msm8916/init.c
void target_early_init(void)
{
#if WITH_DEBUG_UART
uart_dm_init(2, 0, BLSP1_UART1_BASE);
#endif
}
uart_dm_init这个函数中,第一个参数表示BLSP ID(1-12),第二个参数表示是基于GSBI,第三个参数表示UART CORE的物理地址(路径:bootable/bootloader/lk/platform/msm8916/include/platform/iomap.h)
接下来看一下具体实现:
路径:bootable/bootloader/lk/platform/msm_shared/uart_dm.c
void uart_dm_init(uint8_t id, uint32_t gsbi_base, uint32_t uart_dm_base)
{
static uint8_t port = 0;
char *data = "Android Bootloader - UART_DM Initialized!!!\n";
/* Configure the uart clock */
clock_config_uart_dm(id);
dsb();
/* Configure GPIO to provide connectivity between UART block
product ports and chip pads */
gpio_config_uart_dm(id);
dsb();
/* Configure GSBI for UART_DM protocol.
* I2C on 2 ports, UART (without HS flow control) on the other 2.
* This is only on chips that have GSBI block
*/
if(gsbi_base)
writel(GSBI_PROTOCOL_CODE_I2C_UART <<
GSBI_CTRL_REG_PROTOCOL_CODE_S,
GSBI_CTRL_REG(gsbi_base));
dsb();
/* Configure clock selection register for tx and rx rates.
* Selecting 115.2k for both RX and TX.
*/
writel(UART_DM_CLK_RX_TX_BIT_RATE, MSM_BOOT_UART_DM_CSR(uart_dm_base));
dsb();
/* Intialize UART_DM */
msm_boot_uart_dm_init(uart_dm_base);
msm_boot_uart_dm_write(uart_dm_base, data, 44);
ASSERT(port < ARRAY_SIZE(port_lookup));
port_lookup[port++] = uart_dm_base;
/* Set UART init flag */
uart_init_flag = 1;
}
主要看一下clock_config_uart_dm(id);和gpio_config_uart_dm(id);
路径:bootable/bootloader/lk/platform/msm8916/acpuclock.c
void clock_config_uart_dm(uint8_t id)
{
int ret;
char iclk[64];
char cclk[64];
snprintf(iclk, sizeof(iclk), "uart%u_iface_clk", id);
snprintf(cclk, sizeof(cclk), "uart%u_core_clk", id);
ret = clk_get_set_enable(iclk, 0, 1);
if(ret)
{
dprintf(CRITICAL, "failed to set %s ret = %d\n", iclk, ret);
ASSERT(0);
}
ret = clk_get_set_enable(cclk, 7372800, 1);
if(ret)
{
dprintf(CRITICAL, "failed to set %s ret = %d\n", cclk, ret);
ASSERT(0);
}
}
iclk 是iface_clock,为BLSP的clock;cclk是core_clock,为UART的clock.
在下面路径中定义
路径:bootable/bootloader/lk/platform/msm8916/include/platform/iomap.h
/* UART */
#define BLSP1_AHB_CBCR (CLK_CTL_BASE + 0x1008)
路径:bootable/bootloader/lk/platform/msm8916/msm8916-clock.c
static struct clk_lookup msm_clocks_8916[] =
{
CLK_LOOKUP("uart2_iface_clk", gcc_blsp1_ahb_clk.c),
CLK_LOOKUP("uart2_core_clk", gcc_blsp1_uart2_apps_clk.c),
.......
}
static struct vote_clk gcc_blsp1_ahb_clk = {
.cbcr_reg = (uint32_t *) BLSP1_AHB_CBCR,
.vote_reg = (uint32_t *) APCS_CLOCK_BRANCH_ENA_VOTE,
.en_mask = BIT(10),
.c = {
.dbg_name = "gcc_blsp1_ahb_clk",
.ops = &clk_ops_vote,
},
};
static struct clk_ops clk_ops_vote =
{
.enable = clock_lib2_vote_clk_enable,
.disable = clock_lib2_vote_clk_disable,
};
static struct branch_clk gcc_blsp1_uart2_apps_clk =
{
.cbcr_reg = (uint32_t *) BLSP1_UART2_APPS_CBCR,
.parent = &blsp1_uart2_apps_clk_src.c,
.c = {
.dbg_name = "gcc_blsp1_uart2_apps_clk",
.ops = &clk_ops_branch,
},
};
static struct clk_ops clk_ops_branch =
{
.enable = clock_lib2_branch_clk_enable,
.disable = clock_lib2_branch_clk_disable,
.set_rate = clock_lib2_branch_set_rate,
};
时钟配置到这就完了,接下来看一下引脚配置
路径:bootable/bootloader/lk/platform/msm8916/gpio.c
void gpio_config_uart_dm(uint8_t id)
{
/* configure rx gpio */
gpio_tlmm_config(5, 2, GPIO_INPUT, GPIO_NO_PULL,
GPIO_8MA, GPIO_DISABLE);
/* configure tx gpio */
gpio_tlmm_config(4, 2, GPIO_OUTPUT, GPIO_NO_PULL,
GPIO_8MA, GPIO_DISABLE);
}
到此,UART在lk的配置就结束了