day42-ARM引脚、串口

day42-ARM引脚、串口

一、ARM引脚控制

在裸机编程中,想要控制引脚就需要配置该引脚相应的寄存器

1.1 pwm控制无源蜂鸣器

  1. 看原理图
    本例采用fs4412开发板,pwm外接了一个蜂鸣器BUZZER,电路图如下:
    beep

继续查找pwm的引脚:

pwm引脚

可知要配置的是GPD0_0。

再看pwm的内部模块以此知道要配置的寄存器:
pwm内部

工作的步骤:

  1. 当时钟PCLK被使能后,定时器计数缓冲寄存器(TCNTBn)把计数器初始值下载到递减计数器中。
  2. 定时器比较缓冲寄存器(TCMPBn)把其初始值下载到比较寄存器中,并将该值与递减计数器的值进行比较。当递减计数器和比较寄存器值相同时,输出电平翻转。
  3. 递减计数器减至0后,输出电平再次翻转,完成一个输出周期。这种基于TCNTBn和TCMPBn的双缓冲特性使定时器在频率和占空比变化时能产生稳定的输出。
  4. 每个定时器都有一个专用的由定时器时钟驱动的16位递减计数器。
    当递减计数器的计数值达到0时,就会产生定时器中断请求来通知CPU定时器操作完成。当定时器递减计数器达到0的时候,如果设置了Auto-Reload 功能,相应的TCNTBn的值会自动重载到递减计数器中以继续下次操作。
  5. 然而,如果定时器停止了,比如在定时器运行时清除TCON中定时器使能位,TCNTBn的值不会被重载到递减计数器中。
  6. TCMPBn 的值用于脉冲宽度调制。当定时器的递减计数器的值和比较寄存器的值相匹配的时候,定时器控制逻辑将改变输出电平。因此,比较寄存器决定了PWM 输出的开关时间。
  1. 看寄存器
    pwm需要的寄存器如下图(还需要将GPD0CON设置为0x2即TOUT_0,pwm输出模式):
    pwmj

(1)TFCG0
定时器配置寄存器0(TFCG0) ,主要用于预分频设置。
tfcg0

(2)TFCG1
定时器配置寄存器1(TFCG1) ,主要用于预分频设置。
tfcg1

(3)TCON
timer控制寄存器TCON,主要用于使能定时器,设置自动重载,设置输出电平翻转等。
tcon

  • bite[3] : 设置定时器是只执行一个周期(One-shot)还是周期执行(auto-reload)
  • bite[1]: 置为1,则更新TCNTB0 、TCMPB0 的值
  • bit[0]:开启或者停止定时器

(4)TCNTB0
定时器计数缓冲寄存器(TCNTB0),主要用于设置定时器的初始值。
tcntb0

(5)TCMPB0
定时器比较缓冲寄存器(TCMPB0),主要用于设置定时器的比较值。
tcmpb0

3.代码
总体流程如下:
pwmz

代码如下:

  • pwm.c
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    #define GPD0CON (*((volatile unsigned int *)0x114000A0))
    #define TCFG0 (*((volatile unsigned int *)0x139D0000))
    #define TCFG1 (*((volatile unsigned int *)0x139D0004))
    #define TCON0 (*((volatile unsigned int *)0x139D0008))
    #define TCNTB0 (*((volatile unsigned int *)0x139D000c))
    #define TCMPB0 (*((volatile unsigned int *)0x139D0010))

    //1.对外设置
    void PWM_GPIO_Init(void)
    {

    //GPD0_0: TOUT0
    GPD0CON &= ~0xf;
    GPD0CON |= 0x2;
    }

    //2.对内设置
    void PWM_Init(void)
    {
    TCFG0 = 255;//第一次分频 分频值:255+1

    TCFG1 &= ~0xf; //第二次分频 16分频
    TCFG1 |= 0x4;

    TCNTB0 = 500;//计数周期值
    TCMPB0 = 250;//比较值

    TCON0 &= ~0xf;
    TCON0 |= 0xe;//auto reload:1 manual update: 1
    TCON0 &= 0xc;//auto reload:1 manual update: 0

    }

    //3.buzzer on
    void buzzer_on(void)
    {
    TCON0 |= 0x1;
    }

    //4.buzzer off
    void buzzer_off(void)
    {
    TCON0 &= ~0x1;
    }
  • main.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main(int argc, char *argv[])
{


PWM_GPIO_Init();
PWM_Init();

buzzer_on();//开buzzer

while(1)
{

}
return 0;
}


-start.s

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
    .global  delay1s 
.text
.global _start
_start:
b reset @0x00
ldr pc,_undefined_instruction @0x04
ldr pc,_software_interrupt
ldr pc,_prefetch_abort
ldr pc,_data_abort
ldr pc,_not_used
ldr pc,_irq
ldr pc,_fiq

_undefined_instruction: .word _undefined_instruction
_software_interrupt: .word _software_interrupt
_prefetch_abort: .word _prefetch_abort
_data_abort: .word _data_abort
_not_used: .word _not_used
_irq: .word _irq
_fiq: .word _fiq


reset:
ldr r0,=0x40008000 @设置异常向量表的起始地址为0x40008000
mcr p15,0,r0,c12,c0,0 @ Vector Base Address Register 修改异常向量表的起始地址

init_stack:
ldr r0,stacktop /*get stack top pointer*/

/********svc mode stack********/
mov sp,r0
sub r0,#128*4 /*512 byte for irq mode of stack*/
/****irq mode stack**/
msr cpsr,#0xd2
mov sp,r0
sub r0,#128*4 /*512 byte for irq mode of stack*/
/***fiq mode stack***/
msr cpsr,#0xd1
mov sp,r0
sub r0,#0
/***abort mode stack***/
msr cpsr,#0xd7
mov sp,r0
sub r0,#0
/***undefine mode stack***/
msr cpsr,#0xdb
mov sp,r0
sub r0,#0
/*** sys mode and usr mode stack ***/
msr cpsr,#0x10
mov sp,r0 /*1024 byte for user mode of stack*/

b main

delay1s:
ldr r4,=0x1ffffff
delay1s_loop:
sub r4,r4,#1
cmp r4,#0
bne delay1s_loop
mov pc,lr


.align 4

/**** swi_interrupt handler ****/


stacktop: .word stack+4*512

.data

stack:
.space 4*512
.end
  • makefile
1
2
3
4
5
6
7
8
9
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 pwm.o pwm.c
arm-none-linux-gnueabi-ld *.o -Tmap.lds -o buzzer.elf
arm-none-linux-gnueabi-objcopy -O binary buzzer.elf buzzer.bin
arm-none-linux-gnueabi-objdump -D buzzer.elf > buzzer.dis
clean:
rm -rf *.bak *.o *.elf *.dis *.bin
  • map.lds
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/*linux下的连接脚本模板*/
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") /*指定输出可执行文件是elf格式,32位ARM指令,小端*/
OUTPUT_ARCH(arm) /*指定输出可执行文件的平台(arm平台)*/
ENTRY(_start) /*指定连接之后第一条指令的地址为_start*/
SECTIONS /*指定连接之后的代码段(.text) 数据段(.data) .bss段如何摆放*/
{
. = 0x40008000; /*指定链接的起始地址 从0x40008000地址开始摆放*/
. = ALIGN(4); /*指令对齐(4字节对齐)*/
.text : /*代码段开始*/
{
start.o(.text) /*0x40008000地址放start.o对应的start.s的第一条指令*/
*(.text) /* *:其他的*.o文件系统自动安排位置*/
}
. = ALIGN(4);
.data : /*数据段开始*/
{ *(.data) } /*数据段也让系统自动分配*/
. = ALIGN(4);
.bss :
{ *(.bss) }
}



1.2 串口控制

  • uart.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#define GPA1CON (*((volatile unsigned int *)0x11400020))
#define ULCON2 (*((volatile unsigned int *)0x13820000))
#define UCON (*((volatile unsigned int *)0x13820004))
#define UTRSTAT2 (*((volatile unsigned int *)0x13820010))
#define UTXH2 (*((volatile unsigned int *)0x13820020))
#define URXH2 (*((volatile unsigned int *)0x13820024))
#define UBRDIV2 (*((volatile unsigned int *)0x13820028))
#define UFRACVAL2 (*((volatile unsigned int *)0x1382002c))



//1.对外设置
void GPIO_Init()
{
//GPA1CON: 7:0->0x22 uart管脚
GPA1CON &= ~0xff; //7:0清0
GPA1CON |= 0x22; // 7:0->0x22
}

//2.对内设置 uart设置
void UART_Init()
{
//1.ULCON2: data:8 stop:1 Bau:115200 parity:无

ULCON2 = 0x3;

//2.UCON: 设置发送接收方式 polling
UCON &= ~0xf;
UCON |= 0x5;

//3.设置波特率 115200
UBRDIV2 = 53;
UFRACVAL2 = 4;
}

//3.串口发送
void uart_send(char ch)
{
while((UTRSTAT2 & 0x2) == 0);//等发送数据寄存器为空
UTXH2 = ch;

}

//4.串口接收
char uart_recv(void)
{
char ch;

while((UTRSTAT2 & 0x1) == 0);//等接收数据寄存器不为空
ch = URXH2;
}

//5.发送字符串
void puts(char *str)
{
char *ptr = str;
while(*ptr)
{
uart_send(*ptr);
ptr++;
}
}


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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

#include "uart.c"

int main(int argc, char *argv)
{
GPIO_Init();
UART_init();

char rev;
while (1)
{
rev = uart_recv();
uart_send(rev);
if (rev == '1')
{
// 将寄存器GPX2DAT的第7位置1
GPX2DAT |= 0x80;
}
else if (rev == '0')
{
GPX2DAT &= ~0x80;
}
mydelay(100);
}
return 0;
}



1.3 adc

  • adc.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#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; //12bits分辨率
ADCCON |= 0x1 << 14;//分频使能

ADCCON &= ~(0xff << 6);//13:6清0
ADCCON |= 0xff << 6;//分频值: 255+1

ADCCON &= ~(0x1 << 2);//正常模式

ADCMUX = 0x3;//选择AIN3
}

int read_adc(void)
{
int data = 0;
int voltage = 0;
//1.开始转换
ADCCON |= 0x1;

//2.开始读
while((ADCCON & (0x1 <<15)) == 0);//等待转换完成
data = ADCDAT & 0xfff; //读取转换结果

voltage = 1800 * data / 4096; //电压

return voltage;

}




1.4 中断编程

总的流程图如下:

irq

  • irq.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
#define GPX1CON (*(volatile unsigned long *)0x11000C20)
#define GPX1DAT (*(volatile unsigned long *)0x11000C24)
#define EXT_INT41_CON (*(volatile unsigned long *)0x11000E04)
#define EXT_INT41_MASK (*(volatile unsigned long *)0x11000F04)

#define ICDISER_CPU (*(volatile unsigned long *)0x10490104)
#define ICDIPTR14_CPU0 (*(volatile unsigned long *)0x10490838)
#define ICDDCR (*(volatile unsigned long *)0x10490000)
#define ICCICR_CPU0 (*(volatile unsigned long *)0x10480000)
#define ICCPMR_CPU0 (*(volatile unsigned long *)0x10480004)

#define EXT_INT41_PEND (*(volatile unsigned long *)0x11000F44)
#define ICCIAR (*(volatile unsigned long *)0x1048000C)
#define ICCEOIR (*(volatile unsigned long *)0x10480010)
#define ICDICPR_CPU (*(volatile unsigned long *)0x10490284)
void IRQ_Init()
{
GPX1CON |= 0xf << 8; // EINT10
EXT_INT41_CON &= ~(0x7 << 8);
EXT_INT41_CON |= 0x2 << 8; // falling edge
EXT_INT41_MASK &= ~(0x1 << 2);

ICDISER_CPU |= 0x1 << 26; // 使能中断
ICDIPTR14_CPU0 = 0x01010101; // 分发器
ICDDCR |= 0x1; // 分发器使能
ICCICR_CPU0 |= 0x1; // cpu接口使能
ICCPMR_CPU0 = 0xff; // 优先级掩码
}

void irq_handler(void)
{
int irq_no = -1;
int i = 0xffff; // 消抖
while (i > 0)
{
i--;
}
if (GPX1DAT & 0x1 << 2)
{

irq_no = ICCIAR & 0x3ff; // 取出中断号

switch (irq_no)
{
case 57:

break;
case 58:
uart_send('a');

EXT_INT41_PEND |= 0x1 << 2; // 标准中断已发生
ICDICPR_CPU |= 0x1 << 26; // 清除中断挂起
break;
default:
break;
}
}

ICCEOIR &= ~0x3ff; // 清除中断号
ICCEOIR |= irq_no;
}
Donate
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2020-2024 nakano-mahiro
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信