单片机
返回首页

第4课:UART串口编程

2016-08-06 来源:eefocus

首先明确一点:我们学习的串口是异步串口。在传输时,他们各自有各自的时钟。就是我们说的波特率。

我们学习的RS232与UART的区别是,UART使用标准的TTL/COMS电平 进过一个芯片使它的高低电平从TTL中0与3.3V 变成了 低电平5v到15v

高电平-3v到-12v。

首先说一下串口的数据帧格式。它由一个开始位,数据位,校验位和停止位组成。

平时数据处于1状态。

当要开始发送时,从UART改变TxD数据变成0状态1个位的时间,在接受端到0之后的1.5位的时间,接收端开始接受数据。

数据位分为5,6,7,8。四种类型的数据位。之后就是校验位站1位,可以设置也可以不设置。最后的是停止位。可以是1位,1.5位,2位。这个是高电平1。

第4课:UART串口编程 - zcwell@126 - 《ARM嵌入式系统开发-软件设计与优化》

 

UART可以用中断或DMA来工作。它有3个单独的通道。它由4部分组成,发送器,接收器,波特率发生器,控制逻辑组成。

这些部分的设置都是通过寄存器来实现的。

发送的过程是这样的,UART只能通过shifter一位一位的来发数据。它先把要发的数据放到它的缓存FIFO里,当然缓存也可以取消。然后放入shifter里面来发出去。接受也是一样的。通过缓存来接受,然后再通过接受的shifter来接。

具体继电器的设置主要由以下几个:

ULCON  逻辑数据桢格式控制器

UCON  串口的控制继电器

UFCON FIFO控制寄存器

UMCON 串口MODEN控制器 (可以控制AFC 自动流控制)

以下是状态寄存器,用来确定状态的,比如说shifter发送器的状态,接收器的状态。

UTRSTAT 接受发送控制器

UERSTAT 错误状态寄存器

UFSTAT FIFO状态寄存器

最后一个单独的设置寄存器,它用来设置波特率

UBRDIV  波特率发生器

 

以下来写个简单的串口例子。

它不使用FIFO 中断 而直接用shifter收发,采用轮询的方式来检测数据是否发送或被接受。然后通过minicom向开发板发送1表示亮灯,发送2表示熄灯。

文件总共是7个 一个crt0.s main.c addr.h uart.h uart.c uart.lds makefile

crt0.s 是关闭watchdog 并跳转到mian 之后用个deadloop。

.text
.globl _start
_start:
 ldr r0, =0x53000000     @ WATCHDOG close
 mov r1, #0x0
 str r1, [r0]           
 ldr     sp, =1024*4         @set stack,but the capitcy of cache is only 4k

        bl      main      
halt_loop:
            b       halt_loop

第2个是addr.h用来写寄存器的宏定义。

#ifndef ADDR_H
#define ADDR_H
#define GPECON (*(volatile unsigned int *)0x56000040)
#define GPEDAT (*(volatile unsigned int *)0x56000044)

#define GPE12_out (1<<(12*2))
#define GPE13_out (1<<(13*2))

#define GPHCON (*(volatile unsigned int *)0x56000070)
#define GPHUP (*(volatile unsigned int *)0x56000074)
#define ULCON0 (*(volatile unsigned int *)0x50000000)
#define UCON0 (*(volatile unsigned int *)0x50000004)
#define UFCON0 (*(volatile unsigned int *)0x50000008)
#define UMCON0 (*(volatile unsigned int *)0x5000000C)
#define UTRSTAT0 (*(volatile unsigned int *)0x50000010)
#define UFSTAT0 (*(volatile unsigned int *)0x50000018)
#define UTXH0 (*(volatile unsigned int *)0x50000020)
#define URXH0 (*(volatile unsigned int *)0x50000024)
#define UBRDIV0 (*(volatile unsigned int *)0x50000028)

#endif

第3个文件来写uart.h,这是个我们的功能

#ifndef UART_H
#define UART_H
void uart_init();                         //初始化继电器

void uart_write(char *a);           //串口写一行

void uart_read(char *a,int n);   //串口读n个字

void uart_read_line(char *a);   //读一行

void led_on();                          //开灯

void led_off();                          //关灯
#endif

第4个文件具体来写uart.C

#include'uart.h'
#include'addr.h'
#define UART_CLK 50000000                                                                        //我们用的是PCLK 50MHz
#define UART_BAUD_RATE 115200                                                              //比特率是115200
#define UART_BRD (int)(UART_CLK/(UART_BAUD_RATE *16))-1                //计算公式的宏
void uart_init()
{
 GPHCON |=0xa0;                                                                                            //这个是TXD0与RXD0的设置,他们用的是GPH2和3复用的特殊
 GPHUP = 0x0c;                                                                                               //功能,所以还要在这设置上拉电阻,以区别高低电平的。
 ULCON0 = 0x3;                                                                                               //桢格式的设置8个数据位,无校验
 UCON0 = 0x5;                                                                                                 //选择的是中断与轮询模式
 UFCON0 = 0;                                                                                                  //不设FIFO
 UMCON0 = 0;                                                                                                 //不设AFC
 UBRDIV0 = UART_BRD;                                                                                 //设置波特率
}

void uart_write(char *a)
{
 do{
   while(!(UTRSTAT0&2));                                                                            //UTRSTAT0的第2位是1的话表示发送数据的shifter内的数据已经被发送了,现在数据为空。如果里面的数据没有空的话!(U&2)会一直是1,造成一直循环,知道出现UTRSTAT0第2位为1的情况。

 UTXH0 = *(a++);                                                                                         //发送寄存器只能一次最多发8位
}
 while(*a!='\0');
}

void uart_read(char *a,int n)
{
 do{

  while(!(UTRSTAT0&1));                                                                            //第1位为1表示shifter内的数据已经有了,可以读了,如果为0的话将一直循环,知道有数据可读。
  *(a++) = (char)URXH0;
  n--;
 }
 while(n>0);
}

void uart_read_line(char *a)
{
 do{

  while(!(UTRSTAT0&1));
  *(a++) = URXH0;
 }
 while(*a!='\0');
}

void led_on()
{
        GPEDAT = 0;
}

void led_off()
{
 GPEDAT = (3<<12);
}

第5个文件main.c文件

#include'addr.h'
#include'uart.h'

int main()
{
 char *t = 'Welcome to bootloader...\r\n';             //这里只能用指针来传递,指针指向常量字符串的首地址传入。  
 char *t1 = 'press 1 to light loop on\r\n';
 char *t2 ='press 2 to light loop off\r\n';
 char *t3 ='please input your choice\r\n';
 char r[10] ;                                                         //这里指针务必用数组来分配,一个没有malloc函数。
 GPECON = GPE12_out|GPE13_out;
 
 uart_init();
 uart_write(t);
 uart_write(t1);
 uart_write(t2);
 uart_write(t3);
 while(1){
  uart_read(r,1);
  UTXH0 = *r;                                                        //这里是为了回显 
  switch(*r){
   case '1':led_on();break;
   case '2':led_off();break;
  }
 }
 return 0;
}

 

第6个连接文件

SECTIONS {
 . = 0x00;
   .text          :   { *(.text) }
 .rodata ALIGN(4) : {*(.rodata)} 
   .data ALIGN(4) : { *(.data) }
   .bss ALIGN(4)  : { *(.bss)  *(COMMON) }
}
最后介绍下makefile比较好的写法。

SRC := $(wildcard *.c)                                                      //设定直接变量SRC,$(wildcard )表示使用通配符
OBJC := $(patsubst %.c,%.o,$(SRC))                              //表示把SRC里的值是.c结尾的换成.o结尾的结果保存到OBJC中

uart.bin:uart.elf
 arm-linux-objcopy -O binary -S $^ $@                            //表示 $@目标文件 $^所有依赖
 arm-linux-objdump -D -m arm $^ > uart.dis
uart.elf:crt0.o $(OBJC)
 arm-linux-ld -T uart.lds $^ -o $@                                   
%.o:%.c                                                                           //任意.o的依赖为.c 
 arm-linux-gcc  -c $^ -o $@
%.o:%.s    
 arm-linux-gcc  -c $^ -o $@

clean:
 rm -f uart.bin uart.dis uart.elf uart.o crt0.o main.o

然后用jtag烧入0x0内部SRAM的4k容量内,并在minicom里输入数据来控制led

进入单片机查看更多内容>>
相关视频
  • RISC-V嵌入式系统开发

  • SOC系统级芯片设计实验

  • 云龙51单片机实训视频教程(王云,字幕版)

  • 2022 Digi-Key KOL 系列: 你见过1GHz主频的单片机吗?Teensy 4.1开发板介绍

  • TI 新一代 C2000™ 微控制器:全方位助力伺服及马达驱动应用

  • MSP430电容触摸技术 - 防水Demo演示

精选电路图
  • 家用电源无载自动断电装置的设计与制作

  • PIC单片机控制的遥控防盗报警器电路

  • 短波AM发射器电路设计图

  • 使用ESP8266从NTP服务器获取时间并在OLED显示器上显示

  • 如何构建一个触摸传感器电路

  • 基于TDA2003的简单低功耗汽车立体声放大器电路

    相关电子头条文章