作业1
1、编写UART_2串口发送程序时,初始化需要设置哪些参数?
-
时钟使能寄存器地址:
- RCC_APB1: UART的时钟使能寄存器地址,用于使能UART时钟。
- RCC_AHB2: GPIO的A口时钟使能寄存器地址,用于使能GPIOA的时钟。
-
端口基地址:
- gpio_ptr: GPIOA端口的基地址,用于配置GPIO端口的相关设置。
- uart_ptr: UART2端口的基地址,用于配置UART相关寄存器。
-
引脚模式寄存器地址和复用功能寄存器地址:
- gpio_mode: GPIO引脚模式寄存器地址,用于配置引脚模式。
- gpio_afrl: GPIO复用功能低位寄存器,用于配置引脚的复用功能。
-
UART相关寄存器地址:
- uart_cr1: UART控制寄存器1基地址,用于配置UART的工作模式和使能相关功能。
- uart_brr: UART波特率寄存器地址,用于配置波特率。
- uart_isr: UART中断和状态寄存器基地址,用于配置中断相关设置。
- uart_cr2: UART控制寄存器2基地址,用于配置UART的工作模式。
- uart_cr3: UART控制寄存器3基地址,用于配置UART的工作模式。
-
波特率设置参数:
- 根据波特率计算得到的USARTDIV值,写入到UART波特率寄存器(uart_brr)中。
-
其他设置:
- 清除相应的标志位,如中断状态寄存器中的标志位。
- 禁用相应的功能,如关闭UART功能、关闭发送和接收功能。
-
使能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、中断设置可调用函数)。
- 用构件调用方式实现;
- 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");
}
- 用直接地址方式实现
- 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");
}
用构件调用方式实现
用直接地址方式实现
- 当发送数据时,可以看到,左下角提示发送成功,说明数据已经通过串口成功发送,但是接受框并没有接受到返回的数据,后来换了个串口COM9,成功发送数据并接收到返回的数据
- 分析原因:串口配置不匹配
在代码中,flag有什么用?
- 代码中,flag 变量用于标识是否真正收到了一个字节的数据。为了确保处理的数据是有效的当 flag 为1时,表示真正收到了一个字节的数据;
- 当 flag 为0时,表示没有收到有效数据,可能是噪声或者其他干扰。在代码中通过判断 flag 的值来决定是否执行对数据的处理逻辑。
- 在我的代码中,如果 flag 为1,就执行 switch(ch) 的分支逻辑;如果 flag 为0,则不进行任何处理,直接开启总中断等待下一个字节的数据到达用
- 直接访问地址实现UART_2串口的接收程序跟构建调用方式的区别
在直接访问地址代码,我采用了轮询的方式检查UART是否接收到数据,而不是依赖中断触发。通过不断检查状态寄存器,判断是否有数据到达,如果有数据,则读取接收数据寄存器中的数据。同时,在每次检查时还模拟了一个超时机制,如果超过指定次数仍未接收到数据,则跳出循环。这与在构建调用方式中通过中断方式实现UART接收数据有明显的区别。