当前位置:   article > 正文

嵌入式课程学习_作业5_假设速度为115200,系统时钟为72mhz,波特率寄存器brr中的值应该是多少

假设速度为115200,系统时钟为72mhz,波特率寄存器brr中的值应该是多少

前言

这篇文章是关于第六章的作业,在开始作业之前,我们先来总结一下这个第六章我们具体学习了什么,这一章学的是有关串行通信模块以及中断处理程序的使用,首先是串行通信模块,不同于小灯泡的操作,这次我们要操作的是端口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模式,同步时钟,智能卡模式,半双工模式,等等等等,具体配置如下

  1. //设置的参数如下
  2. //uart寄存器相关地址
  3. volatile uint32_t* RCC_AHB2; //GPIO的A口时钟使能寄存器地址
  4. volatile uint32_t* RCC_APB1; //UART的2口时钟使能寄存器地址
  5. volatile uint32_t* gpio_ptr; //GPIO的A口基地址
  6. volatile uint32_t* uart_ptr; //uart2端口的基地址
  7. volatile uint32_t* gpio_mode; //引脚模式寄存器地址=口基地址
  8. volatile uint32_t* gpio_afrl; //GPIO复用功能低位寄存器
  9. volatile uint32_t* uart_brr; //UART波特率寄存器地址
  10. volatile uint32_t* uart_isr; // UART中断和状态寄存器基地址
  11. volatile uint32_t* uart_cr1; //UART控制寄存器1基地址
  12. volatile uint32_t* uart_cr2; // UART控制寄存器2基地址
  13. volatile uint32_t* uart_cr3; // UART控制寄存器3基地址
  14. volatile uint32_t* uart_tdr; // UART发送数据寄存器
  15. uint16_t usartdiv; //BRR寄存器应赋的值
  16. //变量赋值
  17. RCC_APB1=0x40021058UL; //UART时钟使能寄存器地址
  18. RCC_AHB2=0x4002104CUL; //GPIO的A口时钟使能寄存器地址
  19. gpio_ptr=0x48000000UL; //GPIOA端口的基地址
  20. uart_ptr=0x40004400UL; //UART2端口的基地址
  21. gpio_mode=0x48000000UL; //引脚模式寄存器地址=口基地址
  22. gpio_afrl=0x48000020UL; // GPIO复用功能低位寄存器
  23. uart_cr1=0x40004400UL; //UART控制寄存器1基地址
  24. uart_brr=0x4000440CUL; // UART波特率寄存器地址
  25. uart_isr=0x4000441CUL; // UART中断和状态寄存器基地址
  26. uart_tdr=0x40004428UL; //UART发送数据寄存器
  27. uart_cr2=0x40004404UL; // UART控制寄存器2基地址
  28. uart_cr3=0x40004408UL; //UART控制寄存器3基地址
  29. //使能GPIOA和UART2的时钟
  30. *RCC_APB1|=(0x1UL<<17U); //UART2时钟使能
  31. *RCC_AHB2 |=(0x1UL<<0U); //GPIOA时钟使能
  32. //将GPIO端口设置为复用功能
  33. //首先将D7、D6、D5、D4清零
  34. *gpio_mode &= ~((0x3UL<<4U)|(0x3UL<<6U));
  35. //然后将D7、D6、D5、D4设为1010,设置PTA2、PTA3为复用功能串行功能。
  36. *gpio_mode |=((0x2UL<<4U)|(0x2UL<<6U));
  37. //选择引脚的端口复用功能
  38. //首先将D15~D8清零
  39. *gpio_afrl &= ~((0xFUL<<8U)|(0xFUL<<12U));
  40. //然后将D15~D8设置为01110111,分别将PTA3、PTA2引脚设置为USART2_RX、USART2_TX
  41. *gpio_afrl=(((0x1UL<<8U)|(0x2UL<<8U)|(0x4UL<<8U))|((0x1UL<<12U)
  42. |(0x2UL<<12U)|(0x4UL<<12U)));
  43. //暂时禁用UART功能,控制寄存器1的第0位对应的是UE—USART使能位。
  44. //此位清零后,USART预分频器和输出将立即停止,并丢弃所有当前操作。
  45. *uart_cr1 &= ~(0x1UL);
  46. //暂时关闭串口发送与接收功能,控制寄存器1的发送器使能位(D3)、接收器使能位(D2)
  47. *uart_cr1 &= ~((0x1UL<<3U)|(0x1UL<<2U));
  48. //配置波特率
  49. if(*uart_cr1&(0x1UL<<15) == (0x1UL<<15))
  50. usartdiv = (uint16_t)((SystemCoreClock/115200)*2);
  51. else
  52. usartdiv = (uint16_t)((SystemCoreClock/115200));
  53. *uart_brr = usartdiv;
  54. //初始化控制寄存器和中断状态寄存器、清标志位
  55. //关中断
  56. *uart_isr = 0x0UL;
  57. //将控制寄存器2的两个使能位清零。D14—LIN模式使能位、D11—时钟使能位
  58. *uart_cr2 &= ~((0x1UL<<14U)|(0x1UL<<11U));
  59. //将控制寄存器3的三个使能位清零。D5 (SCEN) —smartcard模式使能位、
  60. //D3 (HDSEL) —半双工选择位、D1 (IREN) —IrDA 模式使能位
  61. *uart_cr3 &= ~((0x1UL<<5U) | (0x1UL<<3U) |(0x1UL<<1U));
  62. //启动串口发送与接收功能
  63. *uart_cr1 |= ((0x1UL<<3U)|(0x1UL<<2U));
  64. //开启UART功能
  65. *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号,那么带入到函数中,实例例化就在函数中(这里用代码+注释的方式呈现)

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

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

  1. #define GLOBLE_VAR
  2. #include "includes.h" //包含总头文件
  3. int main(void)
  4. {
  5. //关总中断
  6. DISABLE_INTERRUPTS;
  7. //用户外设模块初始化
  8. gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF);
  9. gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF);
  10. gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF);
  11. uart_init(UART_User,115200); //初始化串口模块
  12. //打开使能模块中断
  13. uart_enable_re_int(UART_User); //使能UART_USER模块接收中断功能
  14. //开总中断
  15. ENABLE_INTERRUPTS;
  16. //提示进入串口工具进行操作
  17. printf("=====================================\n");
  18. printf("广州大学 wyw 32106100071!\n");
  19. printf("请进入串口工具COM4进行操作!\n");
  20. printf("=====================================\n");
  21. }

以下是isr.c

  1. #include "includes.h"
  2. //======================================================================
  3. //程序名称:UART_User_Handler
  4. //触发条件:UART_User串口收到一个字节触发
  5. //======================================================================
  6. void UART_User_Handler(void)
  7. {
  8. //【1】声明局部变量
  9. uint8_t ch;
  10. uint8_t flag;
  11. //【2】关总中断
  12. DISABLE_INTERRUPTS;
  13. //【3】读取接到的一个字节
  14. ch=uart_re1(UART_User,&flag); //调用接收一个字节的函数,清接收中断位
  15. //【4】根据flag判断是否真正收到一个字节的数据
  16. if(flag) //有数据
  17. {
  18. if((ch!='G')&&(ch!='R')&&(ch!='B'))
  19. {
  20. uart_send1(UART_User,(ch+1));//回发接收到的字节后一个,加上姓名缩写
  21. uart_send_string(UART_User,(uint8_t *)" wyw ");
  22. gpio_set(LIGHT_RED,LIGHT_OFF);
  23. gpio_set(LIGHT_BLUE,LIGHT_OFF);
  24. gpio_set(LIGHT_GREEN,LIGHT_OFF);
  25. }
  26. if(ch == 'G')
  27. {
  28. uart_send_string(UART_User,(uint8_t *)" GERRN ON!wyw ");
  29. gpio_set(LIGHT_RED,LIGHT_OFF);
  30. gpio_set(LIGHT_BLUE,LIGHT_OFF);
  31. gpio_set(LIGHT_GREEN,LIGHT_ON);
  32. }
  33. if(ch == 'R')
  34. {
  35. uart_send_string(UART_User,(uint8_t *)" RED ON!wyw ");
  36. gpio_set(LIGHT_GREEN,LIGHT_OFF);
  37. gpio_set(LIGHT_BLUE,LIGHT_OFF);
  38. gpio_set(LIGHT_RED,LIGHT_ON);
  39. }
  40. if(ch == 'B')
  41. {
  42. uart_send_string(UART_User,(uint8_t *)" BLUE ON!wyw ");
  43. gpio_set(LIGHT_GREEN,LIGHT_OFF);
  44. gpio_set(LIGHT_RED,LIGHT_OFF);
  45. gpio_set(LIGHT_BLUE,LIGHT_ON);
  46. }
  47. }
  48. //【5】开总中断
  49. ENABLE_INTERRUPTS;
  50. }

可以看到,调用构件的方式,还是比较方便快捷的,相当于我们前面的设置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

  1. #define GLOBLE_VAR
  2. #include "includes.h" //包含总头文件
  3. int main(void)
  4. {
  5. //定义uart寄存器相关地址
  6. volatile uint32_t* RCC_AHB2; //GPIO的A口时钟使能寄存器地址
  7. volatile uint32_t* RCC_APB1; //UART的2口时钟使能寄存器地址
  8. volatile uint32_t* gpio_ptr; //GPIO的A口基地址
  9. volatile uint32_t* uart_ptr; //uart2端口的基地址
  10. volatile uint32_t* gpio_mode; //引脚模式寄存器地址=口基地址
  11. volatile uint32_t* gpio_afrl; //GPIO复用功能低位寄存器
  12. volatile uint32_t* uart_brr; //UART波特率寄存器地址
  13. volatile uint32_t* uart_isr; // UART中断和状态寄存器基地址
  14. volatile uint32_t* uart_cr1; //UART控制寄存器1基地址
  15. volatile uint32_t* uart_cr2; // UART控制寄存器2基地址
  16. volatile uint32_t* uart_cr3; // UART控制寄存器3基地址
  17. volatile uint32_t* uart_tdr; // UART发送数据寄存器
  18. uint16_t usartdiv; //BRR寄存器应赋的值
  19. //变量赋值
  20. RCC_APB1=0x40021058UL; //UART时钟使能寄存器地址
  21. RCC_AHB2=0x4002104CUL; //GPIO的A口时钟使能寄存器地址
  22. gpio_ptr=0x48000000UL; //GPIOA端口的基地址
  23. uart_ptr=0x40004400UL; //UART2端口的基地址
  24. gpio_mode=0x48000000UL; //引脚模式寄存器地址=口基地址
  25. gpio_afrl=0x48000020UL; //GPIO复用功能低位寄存器
  26. uart_cr1=0x40004400UL; //UART控制寄存器1基地址
  27. uart_brr=0x4000440CUL; //UART波特率寄存器地址
  28. uart_isr=0x4000441CUL; //UART中断和状态寄存器基地址
  29. uart_tdr=0x40004428UL; //UART发送数据寄存器
  30. uart_cr2=0x40004404UL; //UART控制寄存器2基地址
  31. uart_cr3=0x40004408UL; //UART控制寄存器3基地址
  32. //关总中断
  33. DISABLE_INTERRUPTS;
  34. //用户外设模块初始化
  35. gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF);
  36. gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF);
  37. gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF);
  38. //下面执行uart_init(UART_User,115200);的直接地址方式
  39. //使能GPIOA和UART2的时钟
  40. *RCC_APB1|=(0x1UL<<17U); //UART2时钟使能
  41. *RCC_AHB2 |=(0x1UL<<0U); //GPIOA时钟使能
  42. //将GPIO端口设置为复用功能
  43. //首先将D7、D6、D5、D4清零
  44. *gpio_mode &= ~((0x3UL<<4U)|(0x3UL<<6U));
  45. //然后将D7、D6、D5、D4设为1010,设置PTA2、PTA3为复用功能串行功能。
  46. *gpio_mode |=((0x2UL<<4U)|(0x2UL<<6U));
  47. //选择引脚的端口复用功能
  48. //首先将D15~D8清零
  49. *gpio_afrl &= ~((0xFUL<<8U)|(0xFUL<<12U));
  50. //然后将D15~D8设置为01110111,分别将PTA3、PTA2引脚设置为USART2_RX、USART2_TX
  51. *gpio_afrl=(((0x1UL<<8U)|(0x2UL<<8U)|(0x4UL<<8U))|((0x1UL<<12U)
  52. |(0x2UL<<12U)|(0x4UL<<12U)));
  53. //暂时禁用UART功能,控制寄存器1的第0位对应的是UE—USART使能位。
  54. //此位清零后,USART预分频器和输出将立即停止,并丢弃所有当前操作。
  55. *uart_cr1 &= ~(0x1UL);
  56. //暂时关闭串口发送与接收功能,控制寄存器1的发送器使能位(D3)、接收器使能位(D2)
  57. *uart_cr1 &= ~((0x1UL<<3U)|(0x1UL<<2U));
  58. //一位起始位,八位数据位
  59. *uart_cr1 &= ~((0x1UL << 28U)|(0x1UL << 12U));
  60. //过采样因子为16
  61. *uart_cr1 &= ~(0x1UL << 15U);
  62. //配置波特率
  63. usartdiv = (uint16_t)((SystemCoreClock/115200));
  64. *uart_brr = usartdiv;
  65. //初始化控制寄存器和中断状态寄存器、清标志位
  66. //关中断
  67. *uart_isr = 0x0UL;
  68. //将控制寄存器2的两个使能位清零。D14—LIN模式使能位、D11—时钟使能位
  69. *uart_cr2 &= ~((0x1UL<<14U)|(0x1UL<<11U));
  70. //将控制寄存器3的三个使能位清零。D5 (SCEN) —smartcard模式使能位、
  71. //D3 (HDSEL) —半双工选择位、D1 (IREN) —IrDA 模式使能位
  72. *uart_cr3 &= ~((0x1UL<<5U) | (0x1UL<<3U) |(0x1UL<<1U));
  73. //启动串口发送与接收功能
  74. *uart_cr1 |= ((0x1UL<<3U)|(0x1UL<<2U));
  75. //开启UART功能
  76. *uart_cr1 |= (0x1UL<<0U);
  77. //完成直接地址方式实现使能模块中断
  78. //uart_enable_re_int(UART_User); //使能UART_User模块接收中断功能
  79. //两部分,开放UART接收中断,开中断控制器IRQ中断
  80. //开放UART接收中断(将RXNEIE置为1)
  81. *uart_cr1 |= (0x1UL<<5);
  82. //开中断控制器IRQ中断,这里不属于uart部分,直接调用函数
  83. NVIC_EnableIRQ(USART2_IRQn);
  84. //开总中断
  85. ENABLE_INTERRUPTS;
  86. //提示进入串口工具进行操作
  87. printf("=====================================\n");
  88. printf("广州大学 wyw 32106100071!\n");
  89. printf("请进入串口工具COM4进行操作!\n");
  90. printf("=====================================\n");
  91. }

以下是isr.c

  1. #include "includes.h"
  2. #define GLOBLE_VAR
  3. volatile uint32_t* uart_isr = (volatile uint32_t*)0x4000441CUL; // UART中断和状态寄存器基地址
  4. volatile uint32_t* uart_rdr = (volatile uint32_t*)0x40004424UL; // UART接受数据寄存器
  5. volatile uint32_t* uart_tdr = (volatile uint32_t*)0x40004428UL; // UART发送数据寄存器
  6. void User_SysFun(uint8_t ch);
  7. //======================================================================
  8. //程序名称:UART_User_Handler
  9. //触发条件:UART_User串口收到一个字节触发
  10. //备 注:进入本程序后,可使用uart_get_re_int函数可再进行中断标志判断
  11. // (1-有UART接收中断,0-没有UART接收中断)
  12. //======================================================================
  13. void USART2_IRQHandler(void)
  14. {
  15. uint8_t ch;
  16. uint8_t flag;
  17. uint32_t t;
  18. uint32_t t1;
  19. DISABLE_INTERRUPTS; //关总中断
  20. //接收一个字节的数据
  21. //ch=uart_re1(UART_User,&flag); //调用接收一个字节的函数,清接收中断位
  22. for(t = 0; t < 0xFBBB; t++)//一直查询缓冲区是否有数据
  23. {
  24. //先判断isr状态位,再获取数据
  25. if((*uart_isr)&(1<<5U))//第五位为1
  26. {
  27. ch = *uart_rdr;//从RDR寄存器取数
  28. flag = 1;
  29. *uart_isr &= ~(1<<5U);//第五位清零
  30. break;
  31. }
  32. }
  33. if(t>=0xFBBB)//超过指定次数
  34. {
  35. ch = 0XFF;
  36. flag = 0;
  37. }
  38. if(flag) //有数据
  39. {
  40. if((ch!='G')&&(ch!='R')&&(ch!='B'))
  41. {
  42. //uart_send1(UART_User,(ch+1));//回发接收到的字节
  43. //uart_send_string(UART_User,(uint8_t *)" wyw ");
  44. for (t1 = 0; t1 < 0xFBBB; t++)//查询指定次数
  45. {
  46. //发送缓冲区为空则发送数据
  47. //先判断isr状态位,再获取数据
  48. if ((*uart_isr)&(1<<7U)) //检查第7位是否为一
  49. {
  50. *uart_tdr = (ch+1); //放到发送寄存器
  51. break;
  52. }
  53. }
  54. if (t1 >= 0xFBBB)
  55. return 0; //发送超时,发送失败
  56. gpio_set(LIGHT_RED,LIGHT_OFF);
  57. gpio_set(LIGHT_BLUE,LIGHT_OFF);
  58. gpio_set(LIGHT_GREEN,LIGHT_OFF);
  59. }
  60. if(ch == 'G')
  61. {
  62. //uart_send_string(UART_User,(uint8_t *)" GERRN ON!wyw ");
  63. gpio_set(LIGHT_RED,LIGHT_OFF);
  64. gpio_set(LIGHT_BLUE,LIGHT_OFF);
  65. gpio_set(LIGHT_GREEN,LIGHT_ON);
  66. }
  67. if(ch == 'R')
  68. {
  69. //uart_send_string(UART_User,(uint8_t *)" RED ON!wyw ");
  70. gpio_set(LIGHT_GREEN,LIGHT_OFF);
  71. gpio_set(LIGHT_BLUE,LIGHT_OFF);
  72. gpio_set(LIGHT_RED,LIGHT_ON);
  73. }
  74. if(ch == 'B')
  75. {
  76. //uart_send_string(UART_User,(uint8_t *)" BLUE ON!wyw ");
  77. gpio_set(LIGHT_GREEN,LIGHT_OFF);
  78. gpio_set(LIGHT_RED,LIGHT_OFF);
  79. gpio_set(LIGHT_BLUE,LIGHT_ON);
  80. }
  81. }
  82. ENABLE_INTERRUPTS; //开总中断
  83. }
  84. //内部函数
  85. void User_SysFun(uint8_t ch)
  86. {
  87. //(1)收到的一个字节参与组帧
  88. if(gcRecvLen == 0) gcRecvLen =useremuart_frame(ch,(uint8_t*)gcRecvBuf);
  89. //(2)字节进入组帧后,判断gcRecvLen=0?若为0,表示组帧尚未完成,
  90. // 下次收到一个字节,再继续组帧
  91. if(gcRecvLen == 0) goto User_SysFun_Exit;
  92. //(3)至此,gcRecvLen≠0,表示组帧完成,gcRecvLen为帧的长度,校验序列号后(与
  93. // 根据Flash中倒数一扇区开始的16字节进行比较)
  94. // gcRecvBuf[16]进行跳转
  95. if(strncmp((char *)(gcRecvBuf),(char *)((MCU_SECTOR_NUM-1)*MCU_SECTORSIZE+
  96. MCU_FLASH_ADDR_START),16) != 0)
  97. {
  98. gcRecvLen = 0; //恢复接收状态
  99. goto User_SysFun_Exit;
  100. }
  101. //(4)至此,不仅收到完整帧,且序号比较也一致, 根据命令字节gcRecvBuf[16]进行跳转
  102. //若为User串口程序更新命令,则进行程序更新
  103. switch(gcRecvBuf[16]) //帧标识
  104. {
  105. case 0:
  106. SYSTEM_FUNCTION((uint8_t *)(gcRecvBuf+17));
  107. gcRecvLen = 0; //恢复接收状态
  108. break;
  109. default:
  110. break;
  111. }
  112. User_SysFun_Exit:
  113. return;
  114. }

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

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

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

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

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

总结

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/718215
推荐阅读
相关标签
  

闽ICP备14008679号