串行通信的特点:数据以字节为单位,按位的优先级来进行传送
通信涉及通信协议
通信协议按照时间来分:可分为同步通信和异步通信
按照发送数据的位宽来分可分为:串行通信和并行通信
串行通信又分为单工,半双工,全双工
同步通信是面向比特的传输
异步通信是面向字符的传输,需要事先约定波特率
异步通信的空闲格式为1,发送器通过0来表示传送开始,随后传送数据位,再然后发送一到两位的停止位,来停止。
从开始位到停止位称为一帧。
因为没发送一个数据都要发送一个开始位,所以称为异步
串行通信的波特率:
位长:称为位的持续时间,其倒数就是单位时间内传递的位数。人们把每秒传送的位数称为波特率
看了一下数据手册UART 的配置寄存器好多
看了一下书:
UART寄存器初始化步骤:
使能UART时钟
设置UART的服用引脚
先禁止UART发送和接收,以便后续配置UART
设置UART的数据格式,奇偶校验位–停止位数固定为1
设置UART的波特率
如果需要FIFO,还要使能FIFO
使能UART发送和接收
—配置过程好复杂··
PLL锁相环,可以将晶振输出频率Fosc,倍增几倍,以满足高速运算需要。 在不连接PLL时,CPU时钟和晶振时钟相同,即CCLK = Fosc。 当使能PLL并连接,则CCLK = Fosc * M,M为倍频数。
锁相环和锁频环的锁定以及捕获、同步等概念是有区别的。对锁相环而言,所谓锁定是指VCO频率与同步信号频率完全一致,但允许有稳定相位误差;而对锁频环而言,则在锁定时可允许VCO与同步信号有小的稳态频率误差。
关于晶振:
时钟电路
MK60 的时钟电路包括两部分,一个是芯片的主晶振,用于产生芯片和外设所需要的工作时钟;另外一个是实时时钟RTC 的时钟电路,实时时钟(RTC-Real Time Clock)提供一套计数器在系统上电和关闭操作时对时间进行测量,RTC 消耗的功率非常低。蓝
宙电子的K60 系统板的主晶振使用的是50MHz 的有源晶振。
K60 的RTC 时钟由独立的32.768KHz 振荡器来提供。
void uart_init (UARTn_e uratn, uint32 baud)
{
register uint16 sbr, osr;
uint8 temp;
uint32 sysclk,tmp_baud; //时钟
uint16 tmp_diff=~0,tmp_sbr,diff_abs_baud;
/* 配置 UART功能的 复用管脚 */
switch(uratn)
{
case UART0:
SIM_SCGC4 |= SIM_SCGC4_UART0_MASK; //使能 UART0 时钟
if(UART0_RX_PIN == PTA1)
{
port_init( UART0_RX_PIN, ALT2);
}
else if((UART0_RX_PIN == PTA15) || (UART0_RX_PIN == PTB16) || (UART0_RX_PIN == PTD6) )
{
port_init( UART0_RX_PIN, ALT3);
}
else
{
ASSERT(0); //上诉条件都不满足,直接断言失败了,设置管脚有误?
}
if(UART0_TX_PIN == PTA2)
{
port_init( UART0_TX_PIN, ALT2);
}
else if((UART0_TX_PIN == PTA14) || (UART0_TX_PIN == PTB17) || (UART0_TX_PIN == PTD7) )
{
port_init( UART0_TX_PIN, ALT3);
}
else
{
ASSERT(0); //上诉条件都不满足,直接断言失败了,设置管脚有误?
}
break;
case UART1:
SIM_SCGC4 |= SIM_SCGC4_UART1_MASK;
if((UART1_RX_PIN == PTA18) || (UART1_RX_PIN == PTC3) || (UART1_RX_PIN == PTE1) )
{
port_init( UART1_RX_PIN, ALT3);
}
else
{
ASSERT(0); //上诉条件都不满足,直接断言失败了,设置管脚有误?
}
if((UART1_TX_PIN == PTA19) || (UART1_TX_PIN == PTC4) || (UART1_TX_PIN == PTE0) )
{
port_init( UART1_TX_PIN, ALT3);
}
else
{
ASSERT(0); //上诉条件都不满足,直接断言失败了,设置管脚有误?
}
break;
case UART2:
SIM_SCGC4 |= SIM_SCGC4_UART2_MASK;
if((UART2_RX_PIN == PTD2) || (UART2_RX_PIN == PTD4) || (UART2_RX_PIN == PTE17))
{
port_init( UART2_RX_PIN, ALT3);
}
else if(UART2_RX_PIN == PTE23)
{
port_init( UART2_RX_PIN, ALT4);
}
else
{
ASSERT(0); //上诉条件都不满足,直接断言失败了,设置管脚有误?
}
if((UART2_TX_PIN == PTD3) || (UART2_TX_PIN == PTD5) || (UART2_TX_PIN == PTE16))
{
port_init( UART2_TX_PIN, ALT3);
}
else if(UART2_TX_PIN == PTE22)
{
port_init( UART2_TX_PIN, ALT4);
}
break;
default:
break;
}
//管脚复用完毕
if( uratn == UART0)
{
//选择 FLL 或者 PLL/2 (山外代码使用 PLL,因此为 PLL 的 1/2 )
SIM_SOPT2 &= ~SIM_SOPT2_UART0SRC_MASK; //
// SIM_SOPT2 0100 0000 0000 0100 1000 0000 0000 0100
// SIM_SOPT2_UART0SRC_MASK ~(0000 1100 0000 0000 0000 0000 0000 0000)
// SIM_SOPT2_UART0SRC_MASK 1111 0011 1111 1111 1111 1111 1111 1111
// SIM_SOPT2 0100 0000 0000 0100 1000 0000 0000 0100
// 0x40048000
SIM_SOPT2 |= (0
| SIM_SOPT2_UART0SRC(1) //
| SIM_SOPT2_PLLFLLSEL_MASK
);
//以上为选择PLL时钟
// 设置的时候,应该禁止发送接受
UART0_C2 &= ~(UART0_C2_TE_MASK | UART0_C2_RE_MASK);
//0000 0000 0000 0000 0100 0000 0000 0110
//1000 0100 1100 -- 1100
//0000 0000 0000 0000 0000 0000 0000 0100
UART0_C1 = 0; //默认工作模式
sysclk = pll_clk_mhz * 1000* 1000/2;
//UART 波特率 = UART 模块时钟/((OSR+1) × SBR)
//4 <= OSR <= 32
//SBR = (1 ,8191)
for(temp = 4;temp<=32;temp++)
{
tmp_sbr = sysclk/(baud*(temp+1));
if(tmp_sbr > 0x1FFF)
{
continue;
}
tmp_baud = sysclk/(tmp_sbr*(temp+1));
diff_abs_baud = ABS((int32)(tmp_baud - baud));
if(diff_abs_baud == 0)
{
sbr = tmp_sbr;
osr = temp;
break;
}
if(tmp_diff > diff_abs_baud )
{
tmp_diff = diff_abs_baud;
sbr = tmp_sbr;
osr = temp;
}
//由于不一定是整除,因此还需要 加1 试试
tmp_sbr++;
if(tmp_sbr > 0x1FFF)
{
continue;
}
tmp_baud = sysclk/(tmp_sbr*(temp+1));
diff_abs_baud = ABS((int32)(tmp_baud - baud));
if(diff_abs_baud == 0)
{
sbr = tmp_sbr;
osr = temp;
break;
}
if(tmp_diff > diff_abs_baud )
{
sbr = tmp_sbr;
osr = temp;
}
}
//写 SBR
temp = UART0_BDH & (~UART0_BDH_SBR_MASK); //缓存 清空 SBR 的 UARTx_BDH的值
UART0_BDH = temp | UART0_BDH_SBR(sbr >> 8); //先写入SBR高位
UART0_BDL = UART0_BDL_SBR(sbr); //再写入SBR低位
//写 OSR
temp = UART0_C4 & (~UART0_C4_OSR_MASK) ; //缓存 清空 BRFA 的 UARTx_C4 的值
UART0_C4 = temp | UART0_C4_OSR(osr); //写入BRFA
// 设置完毕后,应该使能发送接受
UART0_C2 |= (UART0_C2_TE_MASK | UART0_C2_RE_MASK);
}
else
{
//设置的时候,应该禁止发送接受
UART_C2_REG(UARTN[uratn]) &= ~(0
| UART_C2_TE_MASK
| UART_C2_RE_MASK
);
//配置成8位无校验模式
//设置 UART 数据格式、校验方式和停止位位数。通过设置 UART 模块控制寄存器 C1 实现;
UART_C1_REG(UARTN[uratn]) = (0
//| UART_C2_M_MASK //9 位或 8 位模式选择 : 0 为 8位 ,1 为 9位(注释了表示0,即8位) (如果是9位,位8在UARTx_C3里)
//| UART_C2_PE_MASK //奇偶校验使能(注释了表示禁用)
//| UART_C2_PT_MASK //校验位类型 : 0 为 偶校验 ,1 为 奇校验
);
sysclk = bus_clk_khz * 1000; //bus时钟
//UART 波特率 = UART 模块时钟 / (16 × (SBR[12:0] ))
//不考虑 BRFA 的情况下, SBR = UART 模块时钟 / (16 * UART 波特率)
sbr = (uint16)(sysclk / (baud * 16));
if(sbr > 0x1FFF)sbr = 0x1FFF; //SBR 是 13bit,最大为 0x1FFF
//写 SBR
temp = UART_BDH_REG(UARTN[uratn]) & (~UART_BDH_SBR_MASK); //缓存 清空 SBR 的 UARTx_BDH的值
UART_BDH_REG(UARTN[uratn]) = temp | UART_BDH_SBR(sbr >> 8); //先写入SBR高位
UART_BDL_REG(UARTN[uratn]) = UART_BDL_SBR(sbr); //再写入SBR低位
/* 允许发送和接收 */
UART_C2_REG(UARTN[uratn]) |= (0
| UART_C2_TE_MASK //发送使能
| UART_C2_RE_MASK //接收使能
//| UART_C2_TIE_MASK //发送中断或DMA传输请求使能(注释了表示禁用)
//| UART_C2_TCIE_MASK //发送完成中断使能(注释了表示禁用)
//| UART_C2_RIE_MASK //接收满中断或DMA传输请求使能(注释了表示禁用)
);
}
//设置是否允许接收和发送中断。通过设置 UART 模块的 C2 寄存器的
//RIE 和 TIE 位实现。如果使能中断,必须首先实现中断服务程序;
}
//这个是关于UART的初始化的一些程序,涉及到的寄存器太多,有些逻辑运算太绕了···
其他的UART API程序基本一个样,就不一一列举了