Keshawn_lu's Blog

嵌入式复习作业汇总

字数统计: 1.9k阅读时长: 9 min
2020/06/21 Share

1、编写一个流水灯程序。已知,有8个LED灯(LED0~7)分别连接到S5PV210引脚的GPA0_0~7。当这些引脚高电平的时候,LED灯亮,反之则灭;两个按键(KEY0,KEY1)分别连接到引脚的GPH0_0, GPH0_1。当按下按键KEY0时,实现LED0->LED1->…->LED7 ->LED0的亮灯循环;当按下按键KEY1时,实现LED0->LED7->…->LED1->LED0的亮灯循环。现启动汇编代码start.S已有,请写出包括引脚初始化在内的其他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
#define GPA0CON  *((volatile unsigned long *)0xE0200000)
#define GPA0DAT *((volatile unsigned long *)0xE0200004)

#define GPH0CON *((volatile unsigned long *)0xE0200C00)
#define GPH0DAT *((volatile unsigned long *)0xE0200C04)

#define GPA0_0_out (1<<(0*4)) //第0位为1
#define GPA0_1_out (1<<(1*4)) //第4位为1
#define GPA0_2_out (1<<(2*4)) //8
#define GPA0_3_out (1<<(3*4)) //12
#define GPA0_4_out (1<<(4*4)) //16
#define GPA0_5_out (1<<(5*4)) //20
#define GPA0_6_out (1<<(6*4)) //24
#define GPA0_7_out (1<<(7*4)) //28

int delay(int time)
{
int i,j;
for(i = 0; i < time; i++)
{
for(j = 0;j < 0xfffff; j++);
}
return 0;
}

int main()
{
int key_val = 0;
int bit;
GPA0CON &= ~0xFFFFFFFF; //与运算,清空 32位 端口控制寄存器
GPA0CON |= (GPA0_0_out | GPA0_1_out | GPA0_2_out | GPA0_3_out | GPA0_4_out | GPA0_5_out | GPA0_6_out | GPA0_7_out ); //LED灯汇总, 输出引脚


//LED指示灯初始化状态都熄灭。端口数据寄存器
GPA0DAT &= ~(0xFF << 0);

// 配置GPH0_0和GPH0_1为输入:key0和key1 按键初始化。
GPH0CON &= ~(0xFF << 0); //最后8位清空

while (1)
{
//取得GPH0DAT的低两位,赋值给key_val
key_val = GPH0DAT & 0x3; //0011

if (key_val) //如果不为0,表示可能有按键按下
{
delay(500000); // 防抖延时
//延时后继续获取GPH0DAT的的低两位
key_val = GPH0DAT & 0x3;

if (key_val) //如果还不为0,表示真正有按键按下
{
// 如果值为0x1(0001)表示key1按下
if (0x01 == key_val){

for(int i = 0; i < 8; i++)
{
GPA0DAT = GPA0DAT ^ (1 << i); //异或 不同为1,相同为0
//GPA0DAT ^= 1 << i; // 点亮toggle LED0~7
delay(100);
}
}
else if (0x02 == key_val){ //0010

for(int i = 7; i >= 0; i--)
{
GPA0DAT = GPA0DAT ^ (1 << i);
//GPA0DAT ^= 1 << i; // 点亮toggle LED7~0
delay(100);
}
}
}
}
}
return 0;
}

2、编写一个串口数据接收程序。已知S5PV210默认选PCLK为时钟源,且PCLK为66MHz(即此题不需要编写PCLK的初始化程序)。请初始化串口UART0,即让UART0的波特率为115200,正常模式(非红外模式),以中断或轮询模式发生数据,并选择PCLK为串口时钟源,8位数据位,1位停止位,无校验位。然后让UART0串口不断的接收其他设备发送的数据。当接收到的一个字节的数据为0x0f时,引脚GPH0_0为高电平,以让LED0点亮,同时让引脚GPH0_1为低电平;当接收到的一个字节的数据为0xf0时,引脚GPH0_1为高电平,以让LED1点亮,同时让引脚GPH0_0为低电平。现启动汇编代码start.S已有,请写出包括初始化函数在内的其他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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
// GPIO、UART寄存器地址
#define GPH0CON *((volatile unsigned int *)0xE0200C00)
#define GPH0DAT *((volatile unsigned int *)0xE0200C04)

#define ULCON0 *((volatile unsigned int *)0xE2900000)
#define UCON0 *((volatile unsigned int *)0xE2900004)
#define UFCON0 *((volatile unsigned int *)0xE2900008)
#define UTRSTAT0 *((volatile unsigned int *)0xE2900010)
#define UTXH0 *((volatile unsigned int *)0xE2900020)
#define URXH0 *((volatile unsigned int *)0xE2900024)
#define UBRDIV0 *((volatile unsigned int *)0xE2900028)
#define UDIVSLOT0 *((volatile unsigned int *)0xE290002C)




// 初始化UART0(COM1)
void uart0_init(void)
{

GPA0CON &= ~0xFF; //清空

// 配置GPA0_0为UART_0_RXD,GPA0_1为UART_0_TXD
GPA0CON |= 0x22; // bit[3:0]=0010,bit[7:4]=0010 0010 0010 0x22(16进制)

/* 配置数据传输格式
* data:8bits bit[1:0] = 0x3 11
* stop:1bit bit[2] = 0
* parity:no bit[5:3] = 0xx
* mode:normal bit[6] = 0
*/
//8位数据位,1位停止位,无校验位,正常模式
ULCON0 = (0x3<<0) | (0<<2) | (0<<3) | (0<<6); //存疑 0 << 5?

/* 配置UCON0
* Receive Mode: bit[1:0]=01
* TransmitMode: bit[3:2]=01
* Rx Error Status Interrupt Enable: bit[6]=1
* Clock Selection: bit[10]=0
*/
//中断或轮询模式, 右对齐,按位或
UCON0 = (1<<0) | (1<<2) | (1<<6) | (0<<10);

// 不使用FIFO
UFCON0 = 0;

/* 计算波特率(参考手册上面的公式),设置为115200
* PCLK=66MHz
* DIV_VAL = (66000000/(115200 x 16))-1 = 35.8 - 1 = 34.8
* UBRDIV0 = 34(DIV_VAL的整数部分)
* (num of 1's in UDIVSLOTn)/16 = 0.8
* (num of 1's in UDIVSLOTn) = 12
* UDIVSLOT0 = 0xDDDD (参考手册查表)
*/
UBRDIV0 = 34;
UDIVSLOT0 = 0XDDDD;
return;
}
// 发送数据
void putc(unsigned char c)
{
// 查询状态寄存器,等待发送缓存为空 UTRSTAT0[2] 0代表发送器非空
while (! (UTRSTAT0 & (1 << 2)) );
UTXH0 = c; // 写入发送寄存器
return;
}

// 接收数据
unsigned char getc(void)
{
// 查询状态寄存器,等待接收缓存有数据
while (!(UTRSTAT0 & (1 << 0)) ); //直至UTRSTAT0[0] = 1, 数据准备就绪
return (URXH0);
}



#define GPH0_0_out (1<<(0*4))
#define GPH0_1_out (1<<(1*4))

#define GPH0_0_MASK (0xF<<(0*4)) //1111(0xF)
#define GPH0_1_MASK (0xF<<(1*4))

extern void uart0_init(void);

int main(void)
{
char c;

while (1)
{
uart0_init(); // 初始化uart0
c = getc(); // 从串口终端获取一个字符
putc(c); // 回显

// 清bit[3:0]和bit[7:4]
GPH0CON &= ~(GPH0_0_MASK | GPH0_1_MASK); //0000 0000

// 配置GPH0_0和GPH0_1为输出引脚
GPH0CON |= (GPH0_0_out | GPH0_1_out);

GPH0DAT &= ~(0x3<<0); // 向 bit[1:0]写入0熄灭LED0、LED1 0000 0011->1111 1100

if (c == 0X0F){
GPH0DAT &= 0 << 1;
GPH0DAT |= 1 << 0; // GPH0_0高电平,GPH0_1低电平 0001
}

else if (c == 0XF0){
GPH0DAT &= 0 << 0;
GPH0DAT |= 1 << 1; // GPH0_0低电平,GPH0_1高电平 0010
}

}
return 0;
}

3.编写一个稳压电源的控制程序。以下是稳压电源的示意图。高速AD在电压采集的时候,BUSY引脚为低电平,当模数转换完毕时,BUSY引脚为高电平,要求S5PV210采用查询BUSY引脚的方式里判断其是否转换完毕,然后再读取AD数据。电源驱动和可变负载部分,由于负载上的电压不稳定,所以需要S5PV210输出频率为100KHz(100000Hz)的PWM波来稳定输出电压,即根据其占空比稳定负载上的电压值。现要求S5PV210以10us的间隔进行AD的电压采集,并根据读取数据设定PWM的占空比,读取数据和占空比之间的换算方式如下:

​ 占空比=(读取数据)/ 256

即当读取的数据为0x80时,占空比设定50%。

已知S5PV210默认选PCLK_PSYS为定时器0的时钟源,且已知PCLK_PSYS为66.7MHz(即此题不需要编写PCLK的初始化程序)。现启动汇编代码start.S已有,请写出包括定时器初始化函数、中断初始化函数、中断服务函数等在内的其他C语言代码,并给与代码注释。其中占空比的比例值、PWM波的频率值100KHz等设置上允许有误差。

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
/* 配置定时器输入频率,使用timer0
Timer Input Clock Frequency = PCLK / ( {prescaler value + 1} ) / {divider value}
= 66MHz / (40 + 1) / 16 = 100609HZ
*/


void time0_init(void){
TCFG0 |= 40;
TCFG1 = 0x04; // [3:0] 0100 表示16分频
TCNTB0 = 100609;
TCMPB0 = 50304; //占空比50%

TCON |= (1 << 1); //手动更新on
TCON = 0x09; //启动定时器0, 手动更新off, 自动加载 1001
}


extern void IRQ_handle(void);

//使能Timer 0中断
void init_irq(void)
{
TINT_CSTAT |= (1 << 1);
}

//清中断
void clear_irq(void)
{
TINT_CSTAT |= (1 << 5);
}

//初始化中断器
void init_int(void)
{
//选择中断类型
VIC0INTSELECT |= ~(1 << 22);
//清VIC0ADDRESS = 0x0;
VIC0ADDRESS = 0x0;
//设置TIMER0中断对应中断服务程序的入口地址
VIC0VECTADDR21 = (int)IRQ_handle;
//使能中断
VIC0INTENABLE |= (1 << 22);
}

// 清除中断处理函数地址
void clear_vectaddr()
{
VIC0ADDRESS = 0X0;
}

//读中断状态
unsigned long get_irqstatus(void)
{
return VIC0IRQSTATUS;
}


void irq_handler()
{
voliatile unsigned char status = ( (get_irqstatus() & (1 << 21) ) >> 21) & 0x1;
clear_vecaddr();
clear_irq();
if(status == 0x1)
{
time0_init();
}
}



int main(void)
{
GPA0CON &= ~0xFFFFFFFF; //GPA0CON清零
GPD0CON &= ~0xFF; //GPD0_0-1清零
GPD0CON |= 1; //将GPD0_0设置成time0的输出 0001
while(1){
while(!(GPD0DAT &= (1 << 0))){
time0_init();
}
init_irq();
init_int();
}

}
CATALOG