1. S3C6410 UART
S3C6410 的 UART 提供了四个独立的异步串行 I / O (SIO)端口。每个异步串行 I/O(SIO)端口通过中断或者直接存储器存取(DMA)模式来操作。该 UART 使用系统时钟的时间可以支持的比特率最高为 115.2kb/s。每个UART 的通道包含了两个 64 字节收发 FIFO 存储器。
每个UART包含波特率发生器、发送器、接收器和控制单元,波特率发生器的时钟源可以由PCLK, EXT_UCLK0或EXT_UCLK1产生。发送器和接收器包含64字节FIFO和数据移位器。数据写入FIFO,然后复制到发送移位之前被传输。然后数据被传输数据管脚(TxDn)移出。同时,对接收到的数据进行移位从接收数据引脚(RxDn),然后复制到FIFO从移位器。
UART 使用标准的TTL/CMOS 逻辑电平来表示数据,为了增强数据抗干扰能力和提高传输长度,通常将TTL/CMOS 逻辑电平转换为RS-232 逻辑电平,查看原理图可知Tiny6410 使用的是SP3232 芯片,使用的是TX0 和RX0:
通过设置UART 相关寄存器,我们就可以驱动UART 工作,达到发送和接收字符的目的。
2. 代码编写
2.1 start.S
.global _start
_start:
//外设基地址及大小告诉CPU
ldr r0, =0x70000000 //ldr: load
orr r0, r0, #0x13 //0x13=b10011=256M, 参见arm1176jzfs内核参考手册Page3-130
mcr p15,0,r0,c15,c2,4 //把r0的值(包括了外设基地址+外设大小)告诉cpu
//关看门狗
ldr r0, =0x7E004000 //watch dog timer base address
mov r1, #0
str r1, [r0] //disable watch dog. str: Store
//设置栈
ldr sp, =0x0C002000 //S3C6410 的内部8K 的SRAM 被映射到0X0C000000,而ARM 默认的栈是递减的,所以可以让SP 指向0X0C002000
//打开icache
orr r0, r0, #0x00001000 @ set bit 12 (I) I-cache
mcr p15, 0, r0, c1, c0, 0
// 设置时钟
bl clock_init
//调用C函数点灯
bl main
halt:
b halt
2.2 clock.c
#define APLL_LOCK (*((volatile unsigned long *)0x7E00F000))
#define MPLL_LOCK (*((volatile unsigned long *)0x7E00F004))
#define EPLL_LOCK (*((volatile unsigned long *)0x7E00F008))
#define OTHERS (*((volatile unsigned long *)0x7e00f900))
#define CLK_DIV0 (*((volatile unsigned long *)0x7E00F020))
#define ARM_RATIO 0 // ARMCLK = DOUTAPLL / (ARM_RATIO + 1) = 532/(0+1) = 532 MHz
#define MPLL_RATIO 0 // DOUTMPLL = MOUTMPLL / (MPLL_RATIO + 1) = 532/(0+1) = 532 MHz
#define HCLKX2_RATIO 1 // HCLKX2 = HCLKX2IN / (HCLKX2_RATIO + 1) = 532/(1+1) = 266 MHz
#define HCLK_RATIO 1 // HCLK = HCLKX2 / (HCLK_RATIO + 1) = 266/(1+1) = 133 MHz
#define PCLK_RATIO 3 // PCLK = HCLKX2 / (PCLK_RATIO + 1) = 266/(3+1) = 66.5 MHz
// ARMCLK: CPU时钟
// HCLK: AXI/AHB总线外设时钟(MFC、CAM、TV、LCD、MMC、USB、DMA、SDMA)
// PCLK: APB总线外设时钟(GPIO、WDT、UART、PCM、IrDA、ADC、IIC、KeyPAD、RTC)
#define CLK_DIV0_VAL ((ARM_RATIO) | (MPLL_RATIO << 4) | (HCLK_RATIO << 8) | (HCLKX2_RATIO << 9) | (PCLK_RATIO << 12))
//CLK_DIV0:
/*=====================================================================================================================================*/
/* [31:28] [27:24] [23:20] [19:18] [17:16] [15:12] [11:9] [8] [7:5] [4] [3:0] */
/* CLKMFC CLKJPEG CLKCAM CLKSECUR RESERVED PCLK_RATIO HCLKX2_RATIO HCLK_RATIO RESERVED MPLL_RATIO ARM_RATIO */
/*=====================================================================================================================================*/
#define APLL_CON (*((volatile unsigned long *)0x7E00F00C))
#define APLL_CON_VAL ((1<<31) | (266 << 16) | (3 << 8) | (1)) //PLL=1, MDIV=266, PDIV=3, SDIV=1
/*APLL_CON:
/*============================================================================*/
/* [31] [30:26] [25:16] [15:14] [13:8] [7:3] [2:0] */
/* PLL enable Reserved MDIV Reserved PDIV Reserved SDIV */
/*============================================================================*/
// Equation:
// FOUT = MDIV * FIN / (PDIV * 2^SDIV)
//Recommend Values:
/*========================================================*/
/* FIN(MHz) Target FOUT(MHz) MDIV PDIV SDIV */
/* 12 533 266 3 1 */
/*========================================================*/
#define MPLL_CON (*((volatile unsigned long *)0x7E00F010))
#define MPLL_CON_VAL ((1<<31) | (266 << 16) | (3 << 8) | (1)) //PLL=1, MDIV=266, PDIV=3, SDIV=1
/*MPLL_CON:
/*============================================================================*/
/* [31] [30:26] [25:16] [15:14] [13:8] [7:3] [2:0] */
/* PLL enable Reserved MDIV Reserved PDIV Reserved SDIV */
/*============================================================================*/
// Equation:
// FOUT = MDIV * FIN / (PDIV * 2^SDIV)
//Recommend Values:
/*========================================================*/
/* FIN(MHz) Target FOUT(MHz) MDIV PDIV SDIV */
/* 12 533 266 3 1 */
/*========================================================*/
#define CLK_SRC (*((volatile unsigned long *)0x7E00F01C))
void clock_init(void)
{
/* 1. 设置各PLL的LOCK_TIME,使用默认值 */
APLL_LOCK = 0xffff; // APLL_LOCK,供cpu使用
MPLL_LOCK = 0xffff; // MPLL_LOCK,供AHB(存储/中断/lcd等控制器)/APB(看门狗,定时器,SD等)总线上的设备使用
EPLL_LOCK = 0xffff; // EPLL_LOCK,供UART,IIS,IIC使用
/* 2. 设置为异步模式(Asynchronous mode) */
OTHERS &= ~0xc0; //《linux installation for u-boot》3.7中:用MPLL作为HCLK和PCLK的Source是异步(ASYNC)模式,用APLL是同步(SYNC)模式
while ((OTHERS & 0xf00) != 0);
/* 3. 设置分频系数 */
CLK_DIV0 = CLK_DIV0_VAL;
/* 4. 设置PLL,放大时钟 */
APLL_CON = APLL_CON_VAL;
MPLL_CON = MPLL_CON_VAL;
/* 5. 选择PLL的输出作为时钟源 */
CLK_SRC = 0x03;
}
2.3 uart
2.3.1 uart.h
void putchar(char c);
char getchar(void);
void putstr(char * str);
void uart_init(void);
2.3.2 uart.c
// 功能:初始化串口
#include "uart.h"
#define GPACON (*((volatile unsigned long *)0x7F008000))
#define GPADAT (*((volatile unsigned long *)0x7F008004))
#define ULCON0 (*((volatile unsigned long *)0x7F005000))
#define UCON0 (*((volatile unsigned long *)0x7F005004))
#define UFCON0 (*((volatile unsigned long *)0x7F005008))
#define UMCON0 (*((volatile unsigned long *)0x7F00500C))
#define UFSTAT0 (*((volatile unsigned long *)0x7F005018))
#define UTXH0 (*((volatile unsigned long *)0x7F005020))
#define URXH0 (*((volatile unsigned long *)0x7F005024))
#define UBRDIV0 (*((volatile unsigned long *)0x7F005028))
#define UDIVSLOT0 (*((volatile unsigned long *)0x7F00502C))
void uart_init(void)
{
/* 1. 配置引脚 */
GPACON &= ~0xff;
GPACON |= 0x22; //GPA0=b0010=UART RXD[0]; GPA1=b0010=UART TXD[0]
/* 2. 设置数据格式 */
ULCON0 = 0x03; //Normal mode,No Parity, 1 stop bit, 8-bit
//ULCON0:
/*===============================================================*/
/* [7] [6] [5:3] [2] [1:0] */
/* Reserved Infra-Red Parity Stop Bit Word Length */
/*===============================================================*/
// Infra-Red: 0=Normal, 1=Infra-Red Tx/Rx Mode
// Parity: 0xx=No Parity, 100=Odd, 101=Even
// Number of Stop Bit: 0=One Stop Bit, 1=Two Stop Bit
// Word Length: 00=5-bit, 01=6-bit, 10=7-bit 11=8-bit
/* 3. 设置时钟 */
UCON0 = 0x05; // b0000000000101, Clock = PCLK, Disalbe all interrupts, Interrupt or Polling mode
//UCON0:
/*===========================================================================================================*/
/* [11:10] [9] [8] [7] [6] [5] [4] [3:2] [1:0] */
/* Clock Tx Int Rx Int Rx Time Rx Error Status Loop-back Send Break Transmit Receive */
/* Selection Type Type Out Enalbe Interrupt Enable Mode Signal Mode Mode */
/*===========================================================================================================*/
// Clock Selection:
// Select PCLK or EXT_UCLK04) or EXT_UCLK14) clock for the UART baud rate.
// x0 = PCLK : DIV_VAL = (PCLK / (bps x 16) ) -1
// 01 = EXT_UCLK0: DIV_VAL1) = (EXT_UCLK0 / (bps x 16) ) –1
// 11 = EXT_UCLK1: DIV_VAL1) = (EXT_UCLK1 / (bps x 16) ) –1
// Tx Interrupt Type:
// Interrupt request type.
// 0 = Pulse (Interrupt is requested as soon as the Tx buffer becomes empty in Non-FIFO mode or reaches Tx FIFO Trigger Level in FIFO mode.)
// 1 = Level (Interrupt is requested while Tx buffer is empty in Non-FIFO mode or reaches Tx FIFO Trigger Level in FIFO mode.)
// Rx Interrupt Type:
// Interrupt request type.
// 0 = Pulse (Interrupt is requested the instant Rx buffer receives the data in Non-FIFO mode or reaches Rx FIFO Trigger Level in FIFO mode.)
// 1 = Level (Interrupt is requested while Rx buffer is receiving data in Non-FIFO mode or reaches Rx FIFO Trigger Level in FIFO mode.)
// Rx Time Out Enable:
// Enable/Disable Rx time-out interrupts when UART FIFO is enabled.
// The interrupt is a receive interrupt.
// 0 = Disable 1 = Enable
// Rx Error Status Interrupt Enable:
// Enable the UART to generate an interrupt upon an exception, such as a break, frame error, parity error, or overrun error during a receive operation.
// 0 = Do not generate receive error status interrupt.
// 1 = Generate receive error status interrupt.
// Loop-back Mode:
// Setting loop-back bit to 1 cause the UART to enter the loop-back mode. This mode is provided for test purposes only.
// 0 = Normal operation 1 = Loop-back mode
// Send Break Signal:
// Setting this bit causes the UART to send a break during 1 frame time. This bit is automatically cleared after sending the break signal.
// 0 = Normal transmit 1 = Send break signal
// Transmit Mode:
// Determine which function is currently able to write Tx data to the UART transmit buffer register.
// 00 = Disable
// 01 = Interrupt request or polling mode
// 10 = DMA request (DMA_UART0)
// 11 = DMA request (DMA_UART1)
// Receive Mode:
// Determine which function is currently able to read data from UART receive buffer register.
// 00 = Disable
// 01 = Interrupt request or polling mode
// 10 = DMA request (DMA_UART0)
// 11 = DMA request (DMA_UART1)
/* 4. 设置FIFO */
UFCON0 = 0x01; // b00000001, FIFO Enable
//UFCON0:
/*===============================================================================*/
/* [7:6] [5:4] [3] [2] [1] [0] */
/* Tx FIFO Rx FIFO Reserved Tx FIFO Rx FIFO FIFO */
/* Trigger Level Trigger Level Reset Reset Enalbe */
/*===============================================================================*/
// Tx FIFO Trigger Level:
// Determine the trigger level of transmit FIFO.
// 00 = Empty 01 = 16-byte
// 10 = 32-byte 11 = 48-byte
// Rx FIFO Trigger Level:
// Determine the trigger level of receive FIFO.1)
// 00 = 1-byte 01 = 8-byte
// 10 = 16-byte 11 = 32-byte
// Tx FIFO Reset:
// Auto-cleared after resetting FIFO
// 0 = Normal 1= Tx FIFO reset
// Rx FIFO Reset:
// Auto-cleared after resetting FIFO
// 0 = Normal 1= Rx FIFO reset
// FIFO Enable:
// 0 = Disable 1 = Enable
/* 5. 设置控制流 */
UMCON0 = 0; //无控制流
/* 6. 设置波特率 */
UBRDIV0 = 35;
UDIVSLOT0 = 0x1;
// PCLK : DIV_VAL = (PCLK / (bps x 16) ) -1
// = (66.5M/(115200*16))-1
// = 36.08 -1
// = 35.08
// UBRDIV0 = 35
// (num of 1’s in UDIVSLOT0)/16 = 0.08
// then, (num of 1’s in UDIVSLOT0) = 1.28 = 1
// so, UDVISLOT0 = 0x1
}
/* 接收一个字符 */
char getchar(void)
{
while ((UFSTAT0 & 0x7f) == 0); // 如果RX FIFO空,等待; 否则,只要有数据,取出
return URXH0; // 取数据
}
//UFSTATn:
/*========================================================================================*/
/* [15] [14] [13:8] [7] [6] [5:0] */
/* Reserved Tx FIFO Full Tx FIFO Count Reserved Rx FIFO Full Rx FIFO Count */
/*========================================================================================*/
// Tx FIFO Full
// Set to 1 automatically whenever transmit FIFO is full
// during transmit operation
// 0 = 0-byte ≤ Tx FIFO data ≤ 63-byte
// 1 = Full
// Rx FIFO Full
// Set to 1 automatically whenever receive FIFO is full during
// receive operation
// 0 = 0-byte ≤ Rx FIFO data ≤ 63-byte
// 1 = Full
// 当RX FIFO空时, Bit[6] == 0, Bit[5:0] == 000000, Bit[7:0]== b00000000 == 0x00
// 当Rx FIFO满时, Bit[6] == 1, Bit[5:0] == 111111,Bit[7:0]== b01111111 == 0x7f
// Rx FIFO非空非满:Bit[6] == 0, Bit[5:0] == 0xxxxx,Bit[7:0]== b00xxxxxx
/* 发送一个字符 */
void putchar(char c)
{
while (UFSTAT0 & (1<<14)); // 如果TX FIFO满,等待; 否则,只要未满,继续发送数据
UTXH0 = c; // 写数据
}
/* 发送一个字符串 */
void putstr(char * str)
{
while (*str) {
putchar(*str++);
}
}
2.4 main.c
#include "uart.h"
int main()
{
char c;
uart_init();
putstr("Uart Initialized!\n\r");
while (1)
{
c = getchar();
putchar(c);
}
return 0;
}
2.5 Makefile
uart.bin: start.o main.o uart.o clock.o
arm-linux-ld -Ttext 0x50000000 -o uart.elf $^
arm-linux-objcopy -O binary uart.elf uart.bin
arm-linux-objdump -D uart.elf > uart.dis
%.o : %.S
arm-linux-gcc -o $@ $< -c
%.o : %.c
arm-linux-gcc -o $@ $< -c -fno-builtin
clean:
rm *.o *.elf *.bin *.dis -rf