嵌入式课程学习_作业5

前言

这篇文章是关于第六章的作业,在开始作业之前,我们先来总结一下这个第六章我们具体学习了什么,这一章学的是有关串行通信模块以及中断处理程序的使用,首先是串行通信模块,不同于小灯泡的操作,这次我们要操作的是端口A,至于怎么操作嘛,则是跟之前的一样的,找到对应寄存器的地址,根据你的需求,进行对应寄存器的操作,就可以了。除此之外,我们还学习了中断处理程序的编写。

基础知识

在开始我们的作业之前,我们先复习一下课上讲的东西。

串行通信

接收:把单线输入的bit数据变成一个字节的并行数据通过接收引脚送入MCU内部

发送:把需要发送的一个字节的并行数据转换为bit数据通过发送引脚输出

我们的开发板子上面有3组UART(通用异步收发传输器),分别是UART1,UART2,UART3,具体的定义如下

我们编程要用的串行口为UART2,即要用的引脚为PTA2以及PTA3,这个时候我们看看手册,找一找他们的信息。

首先时钟使能这个不必多说,必定是要使用的,一个是UART的时钟使能,一个是GPIOA的时钟使能,这个我们就不展开了,先看看如何找到这两个口,首先是设置GPIOA的mode为复用模式,看引脚名为2 3说明在2 3号引脚,如下

其中可以看到10为复用模式,OK,那么设置了复用模式,那么自然而然就要设置复用功能了,那么复用功能在哪里设置呢,复用功能在哪里看呢?具体就是在参考手册中看以及数据手册中看,如下

这几幅图一放上来,就知道怎么设置了吧?我这里就不再过多说明了,这些是比较基础的工作,下面如果要使用这个模块,还需要进行各种小操作,想什么配置波特率啊,过采样因子等等等等,不过这个,具体来说就是在URAT寄存器组进行操作的,具体如何操作我这里就不放出来了,反正会查表,看得懂英语(会使用翻译软件就行了),数据手册和参考手册都有非常详细的描述的。

中断

中断这个概念在这一两年也是听的够多了,但凡是个操作系统的课,都能再学一遍,这里我就不细说了,那么作为一款芯片,我们开发板上面的确实也有能支持中断的硬件,翻看我们的参考手册,能够看到中断向量表,如下

同时,也能从工程头文件看到简单的中断号表b,如下

不过这些东西都是列出来让大家看看而已啦,我们其实关心的东西是如何写一个中断,总的来说,可以分为几个步骤

首先,我们需要一个中断向量表,这个倒是自然,因为对于CPU来说,中断号的传递过去就是收到中断向量,不过这个倒是不用我们自己写,因为大部分能想到的中断,工程文件都已经帮我们准备好了,如下

在这里我们能够找到UART2口的中断处理函数名称,或者是各式设备的中断处理函数名称,值得留意的是,还能看到默认的Default处理函数,不过这个处理函数就是空操作啦。

接下来就是了解一个东西了,就是提供给中断设置的寄存器组了,如下

为此,工程文件提供了一个结构体,用来方便地访问这些寄存器

OK,知道了这些表,我们就可以接着搞中断了,下一步就是把相关的中断功能给开放,这涉及到UART寄存器组以及NVIC寄存器组的操作了,这里的话我们用工程写好的接口函数来深究一下具体做了什么。如下

可以看到,从代码上看,先是做了UART_CR1寄存器的操作,开放了UART接收中断,然后是对NVIC寄存器组进行操作,具体对NVIC操作我们可以试着追踪一下这个函数,如下

可以看到,函数如上,大致就是设置这个中断使能控制寄存器内容的,至于上面的代码说了什么,我们在作业里细谈,这里只要知道这么一回事就行了。

OK,那么再往下做的就是写自己的中断处理程序了,如果自己没有写,那么会默认的跳到Default函数中,而这个函数是空操作,这样大致就能写好中断了。(别忘了写的时候要注意上面一系列操作都是原子操作,即要开关总中断)下面就是作业时间了。

作业

作业一

1、编写UART_2串口发送程序时,初始化要设置那些参数?

这里我用代码段的方式来呈现,总的来说,要设置GPIOA和UART2时钟,然后设置对应端口的复用功能,配置波特率,过采样因子,LIN模式,同步时钟,智能卡模式,半双工模式,等等等等,具体配置如下

    //设置的参数如下
	//uart寄存器相关地址
    volatile uint32_t* RCC_AHB2;     //GPIO的A口时钟使能寄存器地址
    volatile uint32_t* RCC_APB1;     //UART的2口时钟使能寄存器地址
    volatile uint32_t* gpio_ptr;       //GPIO的A口基地址
    volatile uint32_t* uart_ptr;       //uart2端口的基地址
    volatile uint32_t* gpio_mode;    //引脚模式寄存器地址=口基地址
    volatile uint32_t* gpio_afrl;      //GPIO复用功能低位寄存器
    volatile uint32_t* uart_brr;      //UART波特率寄存器地址
    volatile uint32_t* uart_isr;      // UART中断和状态寄存器基地址
    volatile uint32_t* uart_cr1;      //UART控制寄存器1基地址 
    volatile uint32_t* uart_cr2;      // UART控制寄存器2基地址
    volatile uint32_t* uart_cr3;      // UART控制寄存器3基地址
    volatile uint32_t* uart_tdr;      // UART发送数据寄存器
    uint16_t usartdiv;   //BRR寄存器应赋的值

	//变量赋值
	RCC_APB1=0x40021058UL;   //UART时钟使能寄存器地址
    RCC_AHB2=0x4002104CUL;   //GPIO的A口时钟使能寄存器地址
    gpio_ptr=0x48000000UL;   //GPIOA端口的基地址
    uart_ptr=0x40004400UL;  //UART2端口的基地址
    gpio_mode=0x48000000UL;              //引脚模式寄存器地址=口基地址
    gpio_afrl=0x48000020UL;           // GPIO复用功能低位寄存器
    uart_cr1=0x40004400UL;              //UART控制寄存器1基地址 
    uart_brr=0x4000440CUL;          // UART波特率寄存器地址
    uart_isr=0x4000441CUL;         // UART中断和状态寄存器基地址
    uart_tdr=0x40004428UL;         //UART发送数据寄存器
    uart_cr2=0x40004404UL;      // UART控制寄存器2基地址
    uart_cr3=0x40004408UL;      //UART控制寄存器3基地址

	//使能GPIOA和UART2的时钟
    *RCC_APB1|=(0x1UL<<17U);       //UART2时钟使能 
    *RCC_AHB2 |=(0x1UL<<0U);       //GPIOA时钟使能

	//将GPIO端口设置为复用功能
    //首先将D7、D6、D5、D4清零
    *gpio_mode &= ~((0x3UL<<4U)|(0x3UL<<6U)); 
    //然后将D7、D6、D5、D4设为1010,设置PTA2、PTA3为复用功能串行功能。
    *gpio_mode |=((0x2UL<<4U)|(0x2UL<<6U));

	//选择引脚的端口复用功能
    //首先将D15~D8清零
    *gpio_afrl &= ~((0xFUL<<8U)|(0xFUL<<12U));
    //然后将D15~D8设置为01110111,分别将PTA3、PTA2引脚设置为USART2_RX、USART2_TX 
    *gpio_afrl=(((0x1UL<<8U)|(0x2UL<<8U)|(0x4UL<<8U))|((0x1UL<<12U)
    |(0x2UL<<12U)|(0x4UL<<12U)));    
	
    //暂时禁用UART功能,控制寄存器1的第0位对应的是UE—USART使能位。
    //此位清零后,USART预分频器和输出将立即停止,并丢弃所有当前操作。
    *uart_cr1 &= ~(0x1UL);
    
    //暂时关闭串口发送与接收功能,控制寄存器1的发送器使能位(D3)、接收器使能位(D2)
    *uart_cr1 &= ~((0x1UL<<3U)|(0x1UL<<2U));     

    //配置波特率
    if(*uart_cr1&(0x1UL<<15) == (0x1UL<<15))             
    usartdiv = (uint16_t)((SystemCoreClock/115200)*2);
    else
    usartdiv = (uint16_t)((SystemCoreClock/115200));
    *uart_brr = usartdiv;

    //初始化控制寄存器和中断状态寄存器、清标志位
    //关中断
    *uart_isr = 0x0UL;    
    //将控制寄存器2的两个使能位清零。D14—LIN模式使能位、D11—时钟使能位 
    *uart_cr2 &= ~((0x1UL<<14U)|(0x1UL<<11U));
    //将控制寄存器3的三个使能位清零。D5 (SCEN) —smartcard模式使能位、
    //D3 (HDSEL) —半双工选择位、D1 (IREN) —IrDA 模式使能位
    *uart_cr3 &= ~((0x1UL<<5U) | (0x1UL<<3U) |(0x1UL<<1U));
    
    //启动串口发送与接收功能
    *uart_cr1 |= ((0x1UL<<3U)|(0x1UL<<2U)); 
    
    //开启UART功能
    *uart_cr1 |= (0x1UL<<0U); 

2、假设速度为115200,系统时钟为72MHz,波特率寄存器BRR中的值应该是多少?

我们可以通过这个公式来计算其应该放进去的值

根据过采样因子的不同,有两种情况

  • 过采样因子为8:USARTDIV = 2*72MHz / 115200 = 1250
  • 过采样因子为16:USARTDIV = 72MHz / 115200 = 625

3、中断向量表在哪个文件中?表中有多少项?给出部分截图

中断向量表实际上我在上面就已经展现过了,这里再放一轮吧。

我们能在工程目录中的startup/startup_stm32l431rctx.s下看到中断向量表,如下

其中,我们能够发现有一些为word 0的值,这个实际上是被保留的未曾使用的地址,我在猜想是不是在这里是作者故意保留的,于是翻看了参考手册,发现参考手册这些地址也是被保留的,如下

在我看来,中断向量表保留一些位置是为了满足硬件架构、处理器设计、扩展性、灵活性、安全性和稳定性等方面的需求。这些保留的位置可以根据具体的系统需求进行配置和使用。具体是什么我们也就不深究了。那么第一个问题就回答到这里吧,接下来就是数数了。

在startup_stm32l431rctx.s下,共有74项中断向量(如果保留地址不算数的话),前提是我没数错数啊。

4、以下是中断源使能函数,假设中断源为TIM6,将函数实例例化(写出各项具体数值)。

呃,其实我并不太懂什么叫做实例例化啊,不过大概就是说假设中断源是TIM6,那么这个函数会传些什么数值进去吧,就大概流程是怎么样的。OK,那么一起去做一下吧。

首先我们要知道,中断源为TIM6,那么我们先找一下TIM6的中断号,如下

可以看到,是54号,那么带入到函数中,实例例化就在函数中(这里用代码+注释的方式呈现)

__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)   //IRQn = 54
{
  if ((int32_t)(IRQn) >= 0)     //判断54是否≥0,显然是的,进入if语句块
  {
    //首先我们先看赋值语句左边,这里是将54右移5位,实际上就是整除32,显然得到的是1……x的结果
    //那么实际上赋值语句的左边是NVIC->ISER[1]
    //那为什么是5位呢,因为从上面我们可以得知
    //实际上ISER寄存器组有8个寄存器,每个寄存器实际上是32位,因为我们的中断号是54
    //那么势必是不在0号寄存器的,肯定是1号寄存器的,因此操作的对象自然而然
    //就是是NVIC->ISER[1]了
    NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
    //OK,接下来看赋值语句的右边,那么其实有了左边的详细解释,那么右边肯定是跟余数有关系啦
    //54/32=1……22
    //所以这里是什么呢
    //首先((uint32_t)IRQn) & 0x1FUL)实际上就是保留54的低5位,实际上就是0b10110,十进制就是22
    //然后1UL<<22
    //就是1左移22位呗
    //然后把这个uint32_t的结果赋值给NVIC->ISER[1]
    //实际上就是把寄存器ISER[1]寄存器设置成为"9个0  1 22个0"
    //实际上就是把这个54号中断打开嘛
    //即最后是NVIC->ISER[1]的第22位设为1
  }
}

5、假设将UART_2和TIM6交换其在中断向量表中的位置和IRQ号, UART_2可以正常中断吗?

关于这个题目,我先不打算解释,我们先直接上手试一下。首先需要用到我们简单的工程文件进行操作。首先我们需要一个写好的UART中断处理函数的工程,如下

这里大概就是,如果UART2串口接收到东西,就触发中断,中断处理函数就把这个东西会发到我们的PC端上,OK,那么我们把main.c函数中无关的东西注释掉,毕竟我们只测试中断。先试试看效果如何,如下

可以看到是能正常触发中断处理函数的,OK,接下来我们做一个修改,交换中断号和中断向量,如下

ok,上面的东西都已经交换好了,我们试着在搞一下,看看效果会是如何

我们能发现,这玩意能够发送成功,但是没有触发那个期望的中断处理函数,这也就是说明,至少我发送这个字符串,至少是没有触发isr.c中重写的中断处理函数的,那么我们如何验证有没有产生中断呢?很简单,还记得上面讲的那个如果你自己没写那个中断处理函数,那么就会转到Default中处理,也就是停止。我们验证一下,把main.c的函数重新打开,然后运行的时候往板子发东西,看看还有没有继续发就知道了。如下

显然肯定是产生了中断了,且中断处理函数一定是默认的,为什么,看上图,我点击发送数据,main函数的send函数停下来了,也就是说明进入到了默认的中断处理函数中,且没有出来。OK,这番操作之下,我们不禁思考一个问题,就是下图中

这个中断处理函数,在这种情况下真的是USART2的中断处理函数吗?抱着这样的题目,我们在其下面追加了一个函数,如下

没错,我们重写了TIM6中断处理函数,跟原本的有一点不同,就是发出来的字符是收到的下一位,运行看看效果(把main.c的无关注释掉)

我们发现了一个很抽象的东西,也就是这个中断跑到TIM6的处理函数去了,但我还是想问一句,这个真的是TIM6的硬件产生的中断吗?哈哈,是不是稍微有点眉目了,OK,下面就揭晓。

总的来说UART_2,这个硬件肯定是能产生中断信号的,但关键是硬件的东西早就是写死的,我们所定义的中断号啊,中断向量,这些个宏,仅仅就是对其包装而已,还记得我们做了什么吗,我们交换了中断号的名字(别名),假设说硬件产生的信号就是38,我们原本38的别名就叫“UART2”,自然而然中断向量表就叫的向量名就叫做“USART2_IRQHandler”,这个很符合直觉是吧,因为这是人为定义的,现在我们交换中断向量表的位置和IRQ号,本质上来说就是改了个名字而已,因此实际上再修改之后,USART2_IRQHandler处理函数实际上处理的是TIM6产生的中断,但因为我们本身就没有启动TIM6,那肯定不进入这个函数处理啊,然后本身也没有写TIM6(别名,本质上是处理UART2硬件的中断信号)的处理函数,自然而然就调用默认的处理函数了。也因此我重写了TIM6的中断处理函数,就能正常处理了。

我们在正式点回答,如果UART_2和TIM6在中断向量表中的位置和IRQ号进行了交换,理论上UART_2仍然可以正常中断,前提是其新的中断号未被其他设备占用。扯完这个,再说一下中断向量表是怎么回事,中断向量表是处理器用来识别和处理中断请求的一个表格,每个中断源都有一个对应的中断号和中断服务程序(ISR)。当硬件设备需要处理器注意时,它会通过生成一个中断信号来通知处理器,处理器随后查找中断向量表来确定应当调用哪个ISR来处理该中断。因此,只要中断号正确无误地指向了UART_2的中断处理程序,并且该中断号没有被其他设备使用,UART_2就能够正常地进行中断处理。OK,那么这个问题的回答就到此结束了,不过说实话,研究这个问题还是花费了一点时间的,因为在此之前本人对于中断的认知还不是那么的熟悉。

作业二

1、实现UART_2串口的接收程序,当收到字符时①在电脑的输出窗口显示下一个字符,如收到A显示B;②亮灯:收到字符G,亮绿灯;收到字符R,亮红灯;收到字符B,亮蓝灯;收到其他字符,不亮灯。实现方式:用构件调用方式实现。

经过上面的沉淀,做这种还不是手到擒来,直接上代码!代码分为两部分,一部分是main.c入口函数,一部分是isr.c中断处理函数

以下是main.c

#define GLOBLE_VAR
#include "includes.h"      //包含总头文件

int main(void)
{
	//关总中断
	DISABLE_INTERRUPTS;

	//用户外设模块初始化
	gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF);
    gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF);
    gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF);

	uart_init(UART_User,115200);     //初始化串口模块   
  
	//打开使能模块中断
	uart_enable_re_int(UART_User);  //使能UART_USER模块接收中断功能
	
	//开总中断
	ENABLE_INTERRUPTS;
	
	//提示进入串口工具进行操作
	printf("=====================================\n");
	printf("广州大学 wyw 32106100071!\n");
	printf("请进入串口工具COM4进行操作!\n");
	printf("=====================================\n");
}

以下是isr.c

#include "includes.h"
//======================================================================
//程序名称:UART_User_Handler
//触发条件:UART_User串口收到一个字节触发
//======================================================================
void UART_User_Handler(void)
{
	//【1】声明局部变量
	uint8_t ch;
	uint8_t flag;
    //【2】关总中断
	DISABLE_INTERRUPTS; 
	//【3】读取接到的一个字节
	ch=uart_re1(UART_User,&flag);  //调用接收一个字节的函数,清接收中断位
	//【4】根据flag判断是否真正收到一个字节的数据
	if(flag)                        //有数据
	{
		if((ch!='G')&&(ch!='R')&&(ch!='B'))
		{
			uart_send1(UART_User,(ch+1));//回发接收到的字节后一个,加上姓名缩写
			uart_send_string(UART_User,(uint8_t *)"	wyw   ");
			gpio_set(LIGHT_RED,LIGHT_OFF);
			gpio_set(LIGHT_BLUE,LIGHT_OFF);
			gpio_set(LIGHT_GREEN,LIGHT_OFF);
		}
		if(ch == 'G')
		{
			uart_send_string(UART_User,(uint8_t *)"	GERRN ON!wyw	");
			gpio_set(LIGHT_RED,LIGHT_OFF);
			gpio_set(LIGHT_BLUE,LIGHT_OFF);
			gpio_set(LIGHT_GREEN,LIGHT_ON);
		}
		if(ch == 'R')
		{
			uart_send_string(UART_User,(uint8_t *)"	RED ON!wyw	  ");
			gpio_set(LIGHT_GREEN,LIGHT_OFF);
			gpio_set(LIGHT_BLUE,LIGHT_OFF);
			gpio_set(LIGHT_RED,LIGHT_ON);
		}
		if(ch == 'B')
		{
			uart_send_string(UART_User,(uint8_t *)"	BLUE ON!wyw   ");
			gpio_set(LIGHT_GREEN,LIGHT_OFF);
			gpio_set(LIGHT_RED,LIGHT_OFF);
			gpio_set(LIGHT_BLUE,LIGHT_ON);
		}
			
	}
	//【5】开总中断
	ENABLE_INTERRUPTS;   
 }

可以看到,调用构件的方式,还是比较方便快捷的,相当于我们前面的设置uart中,一个uart_init干了我们很多的东西。代码并没有什么好说的,直接看运行结果吧,如下

可以看到,输出除了RGB字符,输出下一个字符,我这里还加上了输入自身的姓名缩写

可以看到,输入不同的'R','G','B',亮不同的灯,并显示什么灯亮了。完美!

2、实现UART_2串口的接收程序,当收到字符时①在电脑的输出窗口显示下一个字符,如收到A显示B;②亮灯:收到字符G,亮绿灯;收到字符R,亮红灯;收到字符B,亮蓝灯;收到其他字符,不亮灯。实现方式:UART部分用直接地址方式实现(即不调用uart.c中的函数,其他部分如GPIO、中断设置可调用函数)

这里的话,有关于uart的设置要求我们用直接地址方式实现,我的评价是一样的,直接按照步骤一步一步来而已

下面是代码,代码分为两部分,一部分是main.c入口函数,一部分是isr.c中断处理函数

以下是main.c

#define GLOBLE_VAR
#include "includes.h"      //包含总头文件

int main(void)
{
    //定义uart寄存器相关地址
    volatile uint32_t* RCC_AHB2;     //GPIO的A口时钟使能寄存器地址
    volatile uint32_t* RCC_APB1;     //UART的2口时钟使能寄存器地址
    volatile uint32_t* gpio_ptr;       //GPIO的A口基地址
    volatile uint32_t* uart_ptr;       //uart2端口的基地址
    volatile uint32_t* gpio_mode;    //引脚模式寄存器地址=口基地址
    volatile uint32_t* gpio_afrl;      //GPIO复用功能低位寄存器
    volatile uint32_t* uart_brr;      //UART波特率寄存器地址
    volatile uint32_t* uart_isr;      // UART中断和状态寄存器基地址
    volatile uint32_t* uart_cr1;      //UART控制寄存器1基地址 
    volatile uint32_t* uart_cr2;      // UART控制寄存器2基地址
    volatile uint32_t* uart_cr3;      // UART控制寄存器3基地址
    volatile uint32_t* uart_tdr;      // UART发送数据寄存器
    uint16_t usartdiv;   //BRR寄存器应赋的值
    
    //变量赋值
    RCC_APB1=0x40021058UL;   		//UART时钟使能寄存器地址
    RCC_AHB2=0x4002104CUL;   		//GPIO的A口时钟使能寄存器地址
    gpio_ptr=0x48000000UL;   		//GPIOA端口的基地址
    uart_ptr=0x40004400UL;  		//UART2端口的基地址
    gpio_mode=0x48000000UL;         //引脚模式寄存器地址=口基地址
    gpio_afrl=0x48000020UL;         //GPIO复用功能低位寄存器
    uart_cr1=0x40004400UL;          //UART控制寄存器1基地址 
    uart_brr=0x4000440CUL;          //UART波特率寄存器地址
    uart_isr=0x4000441CUL;          //UART中断和状态寄存器基地址
    uart_tdr=0x40004428UL;          //UART发送数据寄存器
    uart_cr2=0x40004404UL;     	 	//UART控制寄存器2基地址
    uart_cr3=0x40004408UL;      	//UART控制寄存器3基地址
    
    //关总中断
    DISABLE_INTERRUPTS;
    
    //用户外设模块初始化
    gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF);
    gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF);
    gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF);
    
    //下面执行uart_init(UART_User,115200);的直接地址方式
    //使能GPIOA和UART2的时钟
    *RCC_APB1|=(0x1UL<<17U);       //UART2时钟使能 
    *RCC_AHB2 |=(0x1UL<<0U);       //GPIOA时钟使能
    
    //将GPIO端口设置为复用功能
    //首先将D7、D6、D5、D4清零
    *gpio_mode &= ~((0x3UL<<4U)|(0x3UL<<6U)); 
    //然后将D7、D6、D5、D4设为1010,设置PTA2、PTA3为复用功能串行功能。
    *gpio_mode |=((0x2UL<<4U)|(0x2UL<<6U));
    
    //选择引脚的端口复用功能
    //首先将D15~D8清零
    *gpio_afrl &= ~((0xFUL<<8U)|(0xFUL<<12U));
    //然后将D15~D8设置为01110111,分别将PTA3、PTA2引脚设置为USART2_RX、USART2_TX 
    *gpio_afrl=(((0x1UL<<8U)|(0x2UL<<8U)|(0x4UL<<8U))|((0x1UL<<12U)
    |(0x2UL<<12U)|(0x4UL<<12U)));         
    
    //暂时禁用UART功能,控制寄存器1的第0位对应的是UE—USART使能位。
    //此位清零后,USART预分频器和输出将立即停止,并丢弃所有当前操作。
    *uart_cr1 &= ~(0x1UL);
    
    //暂时关闭串口发送与接收功能,控制寄存器1的发送器使能位(D3)、接收器使能位(D2)
    *uart_cr1 &= ~((0x1UL<<3U)|(0x1UL<<2U));
    
    //一位起始位,八位数据位
    *uart_cr1 &= ~((0x1UL << 28U)|(0x1UL << 12U));
    
    //过采样因子为16
    *uart_cr1 &= ~(0x1UL << 15U);
    
    //配置波特率
    usartdiv = (uint16_t)((SystemCoreClock/115200));
    *uart_brr = usartdiv;
    
    //初始化控制寄存器和中断状态寄存器、清标志位
    //关中断
    *uart_isr = 0x0UL;    
    //将控制寄存器2的两个使能位清零。D14—LIN模式使能位、D11—时钟使能位 
    *uart_cr2 &= ~((0x1UL<<14U)|(0x1UL<<11U));
    //将控制寄存器3的三个使能位清零。D5 (SCEN) —smartcard模式使能位、
    //D3 (HDSEL) —半双工选择位、D1 (IREN) —IrDA 模式使能位
    *uart_cr3 &= ~((0x1UL<<5U) | (0x1UL<<3U) |(0x1UL<<1U));
    
    //启动串口发送与接收功能
    *uart_cr1 |= ((0x1UL<<3U)|(0x1UL<<2U)); 
    
    //开启UART功能
    *uart_cr1 |= (0x1UL<<0U); 
    
    
    //完成直接地址方式实现使能模块中断
    //uart_enable_re_int(UART_User);  //使能UART_User模块接收中断功能
    //两部分,开放UART接收中断,开中断控制器IRQ中断
    //开放UART接收中断(将RXNEIE置为1)
    *uart_cr1 |= (0x1UL<<5);
    //开中断控制器IRQ中断,这里不属于uart部分,直接调用函数
    NVIC_EnableIRQ(USART2_IRQn);
    
    //开总中断
    ENABLE_INTERRUPTS;
    
   	//提示进入串口工具进行操作
	printf("=====================================\n");
	printf("广州大学 wyw 32106100071!\n");
	printf("请进入串口工具COM4进行操作!\n");
	printf("=====================================\n");  
}

以下是isr.c

#include "includes.h"
#define GLOBLE_VAR

volatile uint32_t* uart_isr = (volatile uint32_t*)0x4000441CUL;	// UART中断和状态寄存器基地址
volatile uint32_t* uart_rdr = (volatile uint32_t*)0x40004424UL;	// UART接受数据寄存器
volatile uint32_t* uart_tdr = (volatile uint32_t*)0x40004428UL;	// UART发送数据寄存器

void User_SysFun(uint8_t ch);
//======================================================================
//程序名称:UART_User_Handler
//触发条件:UART_User串口收到一个字节触发
//备    注:进入本程序后,可使用uart_get_re_int函数可再进行中断标志判断
//          (1-有UART接收中断,0-没有UART接收中断)
//======================================================================
void USART2_IRQHandler(void)
{
	uint8_t ch;
	uint8_t flag;
	uint32_t t;
	uint32_t t1;
	
	DISABLE_INTERRUPTS;   //关总中断
	//接收一个字节的数据
	//ch=uart_re1(UART_User,&flag);  //调用接收一个字节的函数,清接收中断位
	for(t = 0; t < 0xFBBB; t++)//一直查询缓冲区是否有数据
	{	
		//先判断isr状态位,再获取数据
		if((*uart_isr)&(1<<5U))//第五位为1
		{
			ch = *uart_rdr;//从RDR寄存器取数
			flag = 1;
			*uart_isr &= ~(1<<5U);//第五位清零
			break;
		}
	}
	if(t>=0xFBBB)//超过指定次数
	{
		ch = 0XFF;
		flag = 0;
	}
	
	if(flag)                       //有数据
	{
		if((ch!='G')&&(ch!='R')&&(ch!='B'))
		{
			//uart_send1(UART_User,(ch+1));//回发接收到的字节
			//uart_send_string(UART_User,(uint8_t *)"	wyw   ");
			for (t1 = 0; t1 < 0xFBBB; t++)//查询指定次数
			{
				//发送缓冲区为空则发送数据
				//先判断isr状态位,再获取数据
				if ((*uart_isr)&(1<<7U))	//检查第7位是否为一
				{
					*uart_tdr = (ch+1);		//放到发送寄存器
					break;
				}
			}
			if (t1 >= 0xFBBB)
				return 0; //发送超时,发送失败
			gpio_set(LIGHT_RED,LIGHT_OFF);
			gpio_set(LIGHT_BLUE,LIGHT_OFF);
			gpio_set(LIGHT_GREEN,LIGHT_OFF);
		}
		if(ch == 'G')
		{
			//uart_send_string(UART_User,(uint8_t *)"	GERRN ON!wyw	");
			gpio_set(LIGHT_RED,LIGHT_OFF);
			gpio_set(LIGHT_BLUE,LIGHT_OFF);
			gpio_set(LIGHT_GREEN,LIGHT_ON);
		}
		if(ch == 'R')
		{
			//uart_send_string(UART_User,(uint8_t *)"	RED ON!wyw	  ");
			gpio_set(LIGHT_GREEN,LIGHT_OFF);
			gpio_set(LIGHT_BLUE,LIGHT_OFF);
			gpio_set(LIGHT_RED,LIGHT_ON);
		}
		if(ch == 'B')
		{
			//uart_send_string(UART_User,(uint8_t *)"	BLUE ON!wyw   ");
			gpio_set(LIGHT_GREEN,LIGHT_OFF);
			gpio_set(LIGHT_RED,LIGHT_OFF);
			gpio_set(LIGHT_BLUE,LIGHT_ON);
		}
			
	}
	ENABLE_INTERRUPTS;    //开总中断
	
 }

//内部函数
void User_SysFun(uint8_t ch)
{
    //(1)收到的一个字节参与组帧
    if(gcRecvLen == 0)  gcRecvLen =useremuart_frame(ch,(uint8_t*)gcRecvBuf);
    //(2)字节进入组帧后,判断gcRecvLen=0?若为0,表示组帧尚未完成,
    //     下次收到一个字节,再继续组帧
    if(gcRecvLen == 0) goto User_SysFun_Exit;
    //(3)至此,gcRecvLen≠0,表示组帧完成,gcRecvLen为帧的长度,校验序列号后(与
    //     根据Flash中倒数一扇区开始的16字节进行比较)
    //     gcRecvBuf[16]进行跳转
    if(strncmp((char *)(gcRecvBuf),(char *)((MCU_SECTOR_NUM-1)*MCU_SECTORSIZE+
       MCU_FLASH_ADDR_START),16) != 0)
    {
        gcRecvLen = 0;         //恢复接收状态
        goto User_SysFun_Exit;
    }
    //(4)至此,不仅收到完整帧,且序号比较也一致, 根据命令字节gcRecvBuf[16]进行跳转
    //若为User串口程序更新命令,则进行程序更新
    switch(gcRecvBuf[16])  //帧标识
    {
        case 0:
            SYSTEM_FUNCTION((uint8_t *)(gcRecvBuf+17));
            gcRecvLen = 0;         //恢复接收状态
        break;
        default:
        break;
    }
User_SysFun_Exit:
    return;
}

这里的话我就没有向上面构建那样搞什么花里胡哨的输出了,就是按照题目要求,输入一个字符,输出下一个字符,然后RGB就亮灯,不输出,其他字符就灭灯这样子,也就是说,我只用直接地址编程实现了uart.c中的一个uart_rel和uart_send1函数,仅此而已。那么接下来就看看运行效果吧

可以看到,输入A,输出下一个字符B

可以看到,输入RBG,亮对应的灯

可以看到,输入H,输出I,同时灭灯。

完美,成功!那么作业二就到这里为止了

总结

本次作业联动性比较强啊,基本上把串口通信,中断,还有那个之前的点灯全用上了,还是像上次一样,有通过直接地址编程实现,也有调用构件实现,总体下来成就感还是不错的,特别是研究作业一第五题的时候,当我最终把名为TIM6的中断处理程序写好并发现UART中断跑到这里来执行的时候,真的是豁然开朗,醍醐灌顶,哈,先不说这些了,总而言之,通过这次的学习和作业,是我对于中断的理解更加深刻了,同时也对于寄存器直接编程更加得心应手,之前刚开始弄的时候,还要慢吞吞的想如何把那个位置1,清0,现在直接一见到就会写位操作,同时也对于参考手册的使用更加得心应手了,能更快的找到想找的东西了,OK,那么本次作业就到此为止了,下次作业再见!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值