一、串口编程
1.看原理图
GPA1_0: RXD2 GPA1_1: TXD2 |
2.看芯片手册
1)对外设置 (GPIO) GPA1CON: 0x11400020 3:0->0x2(RXD2) 7:4->0x2(TXD2) |
2)对内设置(uart) ULCON2: 0x13820000 0x3 UCON2: 0x13820004 1:0->01(polling) 3:2->01(polling) UTRSTAT2: 0->1(ready read) 1->1(发送完成) UBRDIV2:0x13820028 53 UFRACVAL2:0x1382002C 4 UTXH2: 0x13820020 发送buffer URXH2: 0x13820024 接收buffer |
3.编程
uart.c
#define GPA1CON (*((volatile unsigned int *)0x11400020))
#define ULCON2 (*((volatile unsigned int *)0x13820000))
#define UCON2 (*((volatile unsigned int *)0x13820004))
#define UTRSTAT2 (*((volatile unsigned int *)0x13820010))
#define UBRDIV2 (*((volatile unsigned int *)0x13820028))
#define UFRACVAL2 (*((volatile unsigned int *)0x1382002C))
#define UTXH2 (*((volatile unsigned int *)0x13820020))
#define URXH2 (*((volatile unsigned int *)0x13820024))
void uart_init(void) //初始化
{
//1.对外设置 (GPIO)
GPA1CON &= ~0xff;
GPA1CON |= 0x22; //3:0->0x2(RXD2) 7:4->0x2(TXD2)
//2.对内设置(uart)
ULCON2 = 0x3;//8 1 无
UCON2 &= ~0xf;
UCON2 |= 0x5; // 1:0->01(polling) 3:2->01(polling)
UBRDIV2 = 53;
UFRACVAL2 = 4;//波特率:115200
}
void putc(char ch)
{
UTXH2 = ch;//发送数据
while((UTRSTAT2 & 0x2) == 0);//等待发送完成
}
void puts(char *str)//一个一个发
{
while(*str)
{
putc(*str);
str++;
}
}
char getc(void)
{
char ch;
while((UTRSTAT2 & 0x1) == 0);//等待接收缓冲区有数据
ch = URXH2;//接收数据
}
void put_int(int num)
{
char buf[100];
int i = 0;
if (0 == num)
buf[i++] = '0';
while (num) {
buf[i] = num % 10 + '0';
i++;
num = num / 10;
}
int j, k;
for (j=0,k=i-1; j < k; j++,k--) {
char c = buf[j];
buf[j] = buf[k];
buf[k] = c;
}
buf[i] = '\r';
buf[i+1] = '\n';
buf[i+2] = '\0';
puts(buf);
}
main.c
void mydelay(int x)
{
int i = 0;
while(x--)
for(i=1000;i>0;i--);
}
int main()
{
uart_init(); //初始化
while(1)
{
puts("hello\r\n");
mydelay(500);
}
return 0;
}
练习:串口控制LED 输入0时灯亮 输入1时灯灭
二、PWM
1.看原理图
GPD0_0: XpwmTOUT0 |
2.看芯片手册
1)对外设置(GPIO) GPD0CON: 0x114000A0 3:0->0x2(TOUT0) |
2)对内设置 TCFG0: 0x139D0000 7:0->255 (第一次分频) TCFG1: 0x139D0004 3:0 -> 0100(16分频) TCON: 0x139D0008 0->1/0(启动/关闭定时器) 1->1 1->0(manual off) 2->1(inveter on) 3->1(自动重装载) TCNTB0: 0x139D000C 300(周期值) TCMPB0: 0x139D0010 150(比较值) |
3.编程
pwm.c
#define GPD0CON (*(volatile unsigned int *)0x114000A0)
#define TCFG0 (*(volatile unsigned int *)0x139D0000)
#define TCFG1 (*(volatile unsigned int *)0x139D0004)
#define TCON (*(volatile unsigned int *)0x139D0008)
#define TCNTB0 (*(volatile unsigned int *)0x139D000C)
#define TCMPB0 (*(volatile unsigned int *)0x139D0010)
void pwm_init()
{
//1.对外设置(GPIO)
GPD0CON &= ~0xf;
GPD0CON |= 0x2;//3:0->0x2(TOUT0)
//2.对内设置
TCFG0 &= ~0xff;
TCFG0 |= 255; //第一次分频
TCFG1 &= ~0xf;
TCFG1 |= 0x4;//第二次分频
TCNTB0 = 300;//周期值
TCMPB0 = 150;//比较值
TCON &= ~0xf;
TCON |= 0xe;
TCON &= ~0x2; // 0->0(关闭定时器) 1->1 1->0(manual off) 2->1(inveter on) 3->1(自动重装载)
}
void buzz_on()
{
TCON |= 0x1; //0->1
}
void buzz_off()
{
TCON &= ~0x1; // 0->0
}
三、看门狗定时器
0.作用
看门狗的作用就是防止程序发生死循环,或者说程序跑飞。
在系统运行以后也就启动了看门狗的计数器,看门狗就开始自动计数,如果到了一定的时间还不去清看门狗,那么看门狗计数器就会溢出从而引起看门狗中断,造成系统复位。
1.看原理图
不需要 |
2.看芯片手册
WTCNT:0x10060008 3000 周期值 WTCON: 0x10060000 0->1(reset enable) 4:3->11(128分频) 5->1(WDT enable) 15:8->255 (255分频) |
3.编程
wdt.c
#define WTCNT (*((volatile unsigned int *)0x10060008))
#define WTCON (*((volatile unsigned int *)0x10060000))
void wdt_init()
{
WTCNT = 3000;// 周期值 1s = 1/100MHZ/(255+1)/128 * 3000
WTCON &= ~(0xff<<8);
WTCON |= 255 << 8; //第一次分频 255分频
WTCON |= 0x3 << 3;//第二次分频 128
WTCON |= 0x1; //reset enable
WTCON |= 0x1<<5; // WDT enable
}
四、ADC数模转换
1.看原理图
XadcAIN3 |
2.看芯片手册
ADCCON: 0x126C0000 16->1(12bit的分辨率) 15(转换完成的标志位) 14->1(分频使能) 13:6->19(分频值: 132+1) 2->0(正常模式) 1->1() 0->1(ADC start) ADCDAT:0x126C000C 转换结果 ADCMUX:0x126C001C 3:0->0x3 |
3.编程
adc.c
#define ADCCON (*((volatile unsigned int *)0x126C0000))
#define ADCDAT (*((volatile unsigned int *)0x126C000C))
#define ADCMUX (*((volatile unsigned int *)0x126C001C))
void adc_init(void)
{
ADCCON |= 0x1 << 16;//12bit的分辨率 15(转换完成的标志位)
ADCCON |= 0x1 << 14; //分频使能
ADCCON &= ~(0xff<<6);
ADCCON |= 132 << 6;//分频值: 132+1
ADCCON &= ~(0x1<<2); //正常模式
ADCCON &= ~0x2; //转换完成再去读数据
ADCMUX &= ~0x7;
ADCMUX |= 0x3;//AIN3
}
int readData(void)
{
int data = 0;//保存转换之后的数字量
int voltage = 0;//保存转换之后的电压
ADCCON |= 0x1;//ADC使能
while(ADCCON & (0x1<<15) == 0);//等待转换完成
data = ADCDAT & 0xfff;//转换的数字量
voltage = (1800 * (data+1)) / 4096;//电压值
return voltage;
}
Makefile
all:
arm-none-linux-gnueabi-gcc -fno-builtin -nostdinc -c -o start.o start.s
arm-none-linux-gnueabi-gcc -fno-builtin -nostdinc -c -o main.o main.c
arm-none-linux-gnueabi-gcc -fno-builtin -nostdinc -c -o uart.o uart.c
arm-none-linux-gnueabi-gcc -fno-builtin -nostdinc -c -o pwm.o pwm.c
arm-none-linux-gnueabi-gcc -fno-builtin -nostdinc -c -o wdt.o wdt.c
arm-none-linux-gnueabi-gcc -fno-builtin -nostdinc -c -o adc.o adc.c
arm-none-linux-gnueabi-ld start.o main.o uart.o pwm.o wdt.o adc.o -Tmap.lds -o pwm.elf
arm-none-linux-gnueabi-objcopy -O binary pwm.elf pwm.bin
arm-none-linux-gnueabi-objdump -D pwm.elf > pwm.dis
clean:
rm -rf *.bak *.o *.elf *.dis *.bin
五、中断编程
1.理解 外部设备触发的一种异常; |
2.好处 能够让cpu及时响应外部设备的请求,即马上请求马上响应,提升的硬件的性能。 |
3.处理过程 1)当多个中断同时发生时,CPU如何处理? 优先级 2)当cpu正在处理中断是,突然又来一个同级或低优先级的中断时,cpu如何处理? cpu一定先处理完当前的中断,再去处理新中断; 3)中断标志位 中断处理完之后一定要清除中断标志位。 |
六、按键中断编程(按一下K2 执行打印,需要用到串口)
1.看原理图
GPX1_1: EINT9 port:25 Id:57 |
2.看芯片手册
1)对外设置(GPIO) GPX1CON: 0x11000C20 7:4->0xF //EXT_INT41 EXT_INT41CON: 0x11000E04 6:4->0x2 //下降沿触发 EXT_INT41_MASK: 0x11000F04 1->0 //Enables Interrupt EXT_INT41_PEND: 0x11000F44 1->1 //清除GPIO中断标志位 |
2)对内设置(INT) ICDISER_CPU: 0x10490104 25->1 //总使能 ICDIPTR14:0x10490838 0x01010101 //分发器 ICDDCR: 0x10490000 0->1 //分发使能 ICCICR_CPU0:0x10480000 0->1 //接口使能 ICCPMR_CPU0:0x10480004 255 //优先级 ICCEOIR_CPU0: 0x10480010 9:0->写中断ID //结束中断 ICDICPR_CPU: 0x10490284 25->1 //清除GIC中断标志位 ICCIAR_CPU0: 0x1048000C 9:0->ID //获取中断ID |
3.编程
irq.c
#include "uart.h"
#define GPX1CON (*(volatile unsigned int *)0x11000C20)
#define EXT_INT41CON (*(volatile unsigned int *)0x11000E04)
#define EXT_INT41_MASK (*(volatile unsigned int *)0x11000F04)
#define EXT_INT41_PEND (*(volatile unsigned int *)0x11000F44)
#define ICDISER_CPU (*(volatile unsigned int *)0x10490104)
#define ICDIPTR14 (*(volatile unsigned int *)0x10490838)
#define ICDDCR (*(volatile unsigned int *)0x10490000)
#define ICCICR_CPU0 (*(volatile unsigned int *)0x10480000)
#define ICCPMR_CPU0 (*(volatile unsigned int *)0x10480004)
#define ICCEOIR_CPU0 (*(volatile unsigned int *)0x10480010)
#define ICDICPR_CPU (*(volatile unsigned int *)0x10490284)
#define ICCIAR_CPU0 (*(volatile unsigned int *)0x1048000C)
void irq_init(void)
{
//1.对外设置(GPIO)
GPX1CON |= 0xF << 4; //EXT_INT41
EXT_INT41CON &= ~(0x7<<4);
EXT_INT41CON |= 0x2<<4;//下降沿触发
EXT_INT41_MASK &= ~(0x1<<1); //Enables Interrupt
//2.对内设置(INT)
ICDISER_CPU |= 0x1 << 25; //总使能
ICDIPTR14 = 0x01010101; //分发器
ICDDCR |= 0x1; //分发使能
ICCICR_CPU0 |= 0x1; //接口使能
ICCPMR_CPU0 = 255; //优先级
}
void do_irq(void)//自定义中断处理函数
{
//1.判断是哪个irq
int id = ICCIAR_CPU0 & 0x3ff; //获取中断ID
switch(id)
{
case 57:
putc('i');//处理中断
EXT_INT41_PEND |= 0x1<<1; //清除GPIO中断标志位
ICDICPR_CPU |= 0x1<<25; //清除GIC中断标志位
break;
case 58:
break;
default:
break;
}
ICCEOIR_CPU0 &= ~0x3ff;
ICCEOIR_CPU0 |= id; //结束中断
}
start.s
.text
b reset @0x00 reset
ldr pc,_undef_handler @0x04 undefine
ldr pc, _swi_handler @0x08 swi pc =* _swi_handle
ldr pc, _prefetch_abort_handler @0x0C prefetch abort
ldr pc, _data_abort_handler @0x10 data abort
nop @保留
ldr pc, _irq_handler @irq 0x18
ldr pc, _fiq_handler @fiq 0x1c
_swi_handler:
.word swi_handler
_irq_handler:
.word irq_handler
_fiq_handler:
.word _fiq_handler
swi_handler:
stmfd sp!,{r0-r12,lr} @现场保护
@处理
ldmfd sp!,{r0-r12,pc}^ @cpsr=spsr
irq_handler:
stmfd sp!,{r0-r12,lr} @现场保护
@处理
ldmfd sp!,{r0-r12,pc}^ @cpsr=spsr
reset: @reset异常的处理函数
mov r0,#0x3
mov r1,r0
.end
main.c
void mydelay(int x)
{
int i = 0;
while(x--)
for(i=1000;i>0;i--);
}
int main()
{
irq_init();//irq初始化
uart_init(); //初始化
while(1)
{
putc('a');
mydelay(500);
}
return 0;
}