嵌入式系统-第五次作业(第六章作业1、2)

作业1

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

  1. 时钟使能寄存器地址

    • RCC_APB1: UART的时钟使能寄存器地址,用于使能UART时钟。
    • RCC_AHB2: GPIO的A口时钟使能寄存器地址,用于使能GPIOA的时钟。
  2. 端口基地址

    • gpio_ptr: GPIOA端口的基地址,用于配置GPIO端口的相关设置。
    • uart_ptr: UART2端口的基地址,用于配置UART相关寄存器。
  3. 引脚模式寄存器地址和复用功能寄存器地址

    • gpio_mode: GPIO引脚模式寄存器地址,用于配置引脚模式。
    • gpio_afrl: GPIO复用功能低位寄存器,用于配置引脚的复用功能。
  4. UART相关寄存器地址

    • uart_cr1: UART控制寄存器1基地址,用于配置UART的工作模式和使能相关功能。
    • uart_brr: UART波特率寄存器地址,用于配置波特率。
    • uart_isr: UART中断和状态寄存器基地址,用于配置中断相关设置。
    • uart_cr2: UART控制寄存器2基地址,用于配置UART的工作模式。
    • uart_cr3: UART控制寄存器3基地址,用于配置UART的工作模式。
  5. 波特率设置参数

    • 根据波特率计算得到的USARTDIV值,写入到UART波特率寄存器(uart_brr)中。
  6. 其他设置

    • 清除相应的标志位,如中断状态寄存器中的标志位。
    • 禁用相应的功能,如关闭UART功能、关闭发送和接收功能。
  7. 使能UART功能

    • 启用UART发送和接收功能。
    • 最后,开启UART功能使能位,使UART开始正常工作。

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

USART_BRR寄存器(中的USARTDIV值用来计算串口的通信速率。假设USART_CR1中第15位“过采样”模式为:

过采样位为1:,USARTDIV=2*72000000/115200=1250

过采样位为0:,USARTDIV=72000000/115200=625

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

文件:在MCU目录下的启动代码startup目录下

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

可以看到TIM6中断号是54,所以当IRQn为54,执行函数内的操作,NVIC->ISER[1] = 1 << 22实现将中断位写出到寄存器中,使得中断被使能。

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

不能。将UART2和TIM6在中断向量表中的位置和IRQ号进行交换,中断控制器可能会错误地调用TIM6的中断服务程序来处理UART2的中断请求,或者干脆无法正确识别UART2的中断请求,导致UART2无法正常中断。

作业2

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

2、实现方式: 1、用构件调用方式实现; 2、UART部分用直接地址方式实现(即不调用uart.c中的函数,其他部分如GPIO、中断设置可调用函数)。

  1. 用构件调用方式实现;
  •  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)                        //有数据

    {

        switch (ch)

        {

            case 'G':

                    gpio_set(LIGHT_RED, LIGHT_OFF);

                gpio_set(LIGHT_BLUE, LIGHT_OFF);

                gpio_set(LIGHT_GREEN, LIGHT_ON);

                uart_send_string(UART_User, (uint8_t *)"Green--LJF     ");

                  uart_send_string(UART_User, (uint8_t *)"next char:");

                 uart_send1(UART_User, (ch+1));

                break;

            case 'R':

                gpio_set(LIGHT_GREEN, LIGHT_OFF);

                gpio_set(LIGHT_BLUE, LIGHT_OFF);

                gpio_set(LIGHT_RED, LIGHT_ON);

                uart_send_string(UART_User, (uint8_t *)"Red--LJF         ");

                uart_send_string(UART_User, (uint8_t *)"next char:");

                 uart_send1(UART_User, (ch+1));

                break;

            case 'B':

                gpio_set(LIGHT_GREEN, LIGHT_OFF);

                gpio_set(LIGHT_RED, LIGHT_OFF);

                gpio_set(LIGHT_BLUE, LIGHT_ON);

                uart_send_string(UART_User, (uint8_t *)"Blue--LJF                 ");

                uart_send_string(UART_User, (uint8_t *)"next char:");

                uart_send1(UART_User, (ch+1));

                break;

            default:

                gpio_set(LIGHT_RED, LIGHT_OFF);

                gpio_set(LIGHT_BLUE, LIGHT_OFF);

                gpio_set(LIGHT_GREEN, LIGHT_OFF);

                  uart_send_string(UART_User, (uint8_t *)"next char:");

                uart_send1(UART_User, (ch+1));

                break;

        }

    }

    //【5】开总中断

    ENABLE_INTERRUPTS;  

}



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("32119400035-LJF\n");

         printf("构件调用方式实现UART_2串口的接收程序\n");

         printf("------------------------------------\n");

}
  1. 用直接地址方式实现
  • Isr.c

 

 #include "includes.h"



//======================================================================

//程序名称:UART_User_Handler

//触发条件:UART_User串口收到一个字节触发

//======================================================================



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 UART_User_Handler(void)

{

    //【1】声明局部变量

    uint32_t ch;

    uint8_t flag;

    //【2】关总中断

    DISABLE_INTERRUPTS;

    // 重置计数器

uint32_t timeout_counter = 0;



// 不断检测是否有数据到达或者超时

while (1) {

    // 检查是否有数据到达

    if ((*uart_isr) & (1 << 5U)) { // 第五位为1

        ch = *uart_rdr; // 从 RDR 寄存器取数

        flag = 1;

        *uart_isr &= ~(1 << 5U); // 第五位清零

        break;

    }



    // 延时一段时间,模拟超时

    for (volatile uint32_t i = 0; i < 1000; i++) {

        // 空操作,用于延时

    }



    // 增加超时计数器

    timeout_counter++;



    // 如果超过指定次数,跳出循环

    if (timeout_counter >= 0xFBBB) {

        ch = 0xFF;

        flag = 0;

        break;

    }

}

//【4】根据flag判断是否真正收到一个字节的数据

    if (flag) //有数据

    {

        switch (ch)

        {

            case 'G':

                gpio_set(LIGHT_RED, LIGHT_OFF);

                gpio_set(LIGHT_BLUE, LIGHT_OFF);

                gpio_set(LIGHT_GREEN, LIGHT_ON);

                // 发送字符串

                (*uart_tdr) = 'G';

                break;

            case 'R':

                gpio_set(LIGHT_GREEN, LIGHT_OFF);

                gpio_set(LIGHT_BLUE, LIGHT_OFF);

                gpio_set(LIGHT_RED, LIGHT_ON);

                // 发送字符串

                (*uart_tdr) = 'R';

                break;

            case 'B':

                gpio_set(LIGHT_GREEN, LIGHT_OFF);

                gpio_set(LIGHT_RED, LIGHT_OFF);

                gpio_set(LIGHT_BLUE, LIGHT_ON);

                // 发送字符串

                (*uart_tdr) = 'B';

                break;

            default:

                gpio_set(LIGHT_RED, LIGHT_OFF);

                gpio_set(LIGHT_BLUE, LIGHT_OFF);

                gpio_set(LIGHT_GREEN, LIGHT_OFF);

                // 发送字节

                (*uart_tdr) = ch + 1;

                break;

        }

    }

    //【5】开总中断

    ENABLE_INTERRUPTS;

}





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("32119400035-LJF\n");

         printf("直接地址方式实现UART_2串口的接收程序\n");

         printf("------------------------------------\n");

}

用构件调用方式实现

用直接地址方式实现

  1. 当发送数据时,可以看到,左下角提示发送成功,说明数据已经通过串口成功发送,但是接受框并没有接受到返回的数据,后来换了个串口COM9,成功发送数据并接收到返回的数据
  • 分析原因:串口配置不匹配

在代码中,flag有什么用?

  1. 代码中,flag 变量用于标识是否真正收到了一个字节的数据。为了确保处理的数据是有效的当 flag 为1时,表示真正收到了一个字节的数据;
  2. 当 flag 为0时,表示没有收到有效数据,可能是噪声或者其他干扰。在代码中通过判断 flag 的值来决定是否执行对数据的处理逻辑。
  3. 在我的代码中,如果 flag 为1,就执行 switch(ch) 的分支逻辑;如果 flag 为0,则不进行任何处理,直接开启总中断等待下一个字节的数据到达用
    1.     直接访问地址实现UART_2串口的接收程序跟构建调用方式的区别

在直接访问地址代码,我采用了轮询的方式检查UART是否接收到数据,而不是依赖中断触发。通过不断检查状态寄存器,判断是否有数据到达,如果有数据,则读取接收数据寄存器中的数据。同时,在每次检查时还模拟了一个超时机制,如果超过指定次数仍未接收到数据,则跳出循环。这与在构建调用方式中通过中断方式实现UART接收数据有明显的区别。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值