1.编写UART_2串口发送程序时,初始化需要设置哪些参数?
初始化需要设置的参数主要有:
-
波特率(Baud rate):波特率是指串口通信中每秒钟传输的比特数,它决定了数据传输的速率,在初始化时需要设置一个与通信双方协商好的波特率,确保数据的传输速率一致,以避免数据丢失或错误。
-
数据位(Data bits):数据位指每个数据字节中实际的数据位数,常见的数据位包括8位、7位或者其他,初始化时需要设置数据位,以确保发送和接收的数据字节格式一致。
-
停止位(Stop bits):停止位是在每个数据字节传输结束时发送的特殊位,用于告知接收端一个数据字节的传输已经完成,通常为1位或2位。初始化时需要设置停止位,以确保数据传输的可靠性和准确性。
-
校验位(Parity bits):校验位是一种用于检测数据传输过程中错误的方法。在串口通信中,可以选择是否使用校验位,并选择奇偶校验、偶校验或者不使用校验,初始化时需要设置校验位,以确保数据的完整性和正确性。
-
中断使能(Interrupt Enable):允许触发接收、发送和错误中断,以实现异步数据收发和错误处理。
除此之外一系列UART寄存器也需要进行初始化设置:
时钟使能寄存器地址、端口的基地址寄存器、引脚模式寄存器地址、UART波特率寄存器地址、UART中断和控制寄存器基地址、UART发送/接受寄存器等等。
2、假设速度为115200,系统时钟为72MHz,波特率寄存器BRR中的值应该是多少?
在OVER8 = 1时中波特率寄存器BRR的值应为:
(72MHz/115200) * 2 = 1250
在OVER8 = 0时中波特率寄存器BRR的值为:
(72MHz/115200) = 625
3、中断向量表在哪个文件中?表中有多少项?
中断向量表在stm32l431xx.h文件中,以下是相关中断向量的向量号
表中一共有72项
4、以下是中断源使能函数,假设中断源为TIM6,将函数实例化(写出各项具体数值)。
__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)
{
if ((int32_t)(IRQn) >= 0)
{
NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)IRQn) & 0x1FUL));
}
}
在中断向量表中查看得出TIM6为54
则代入函数中即可得 54>>5UL 可得索引值为1
在1UL<< 54 & 0x1FUL 可得为第22位
即 NVIC->ISER[1]=0000 0000 0100 0000 0000 0000
5、假设将UART_2和TIM6交换其在中断向量表中的位置和IRQ号, UART_2可以正常中断吗?
如果只是交换了UART_2和TIM6的IRQ号,而没有交换它们在中断向量表中的位置,那么UART_2仍然可以正常中断:
-
当UART_2发生中断事件时,即使IRQ号被错误地链接到了TIM6的中断处理程序,但由于中断向量表中UART_2的位置没有变,它的中断处理函数仍然能被正确调用。
-
同样,当TIM6发生中断时,即使IRQ号被错误地链接到了UART_2的中断处理程序,但由于中断向量表中TIM6的位置没有变,它的中断处理函数仍然能被正确调用。
所以在这种情况下,只要中断向量表的映射关系保持正确,UART_2就能够正常响应并处理中断事件。
但如果不仅IRQ号被交换,中断向量表的位置也被交换,那么UART_2就无法正常中断,因为它的中断处理程序将被错误地链接到了TIM6的中断向量。
综上所述, UART_2只有在IRQ号被交换但中断向量表没有被交换的情况下,才能够正常中断。一旦中断向量表的映射关系被错误地交换,UART_2就无法正常工作。
6.运行并理解..\04\-Software\CH06文件夹中的几个程序
在ADDR中代码的含义如下:
主函数开始,首先声明了一些局部变量和指针,然后进行了一系列的初始化设置,包括设置GPIO、UART的时钟使能、引脚复用功能等,初始化完成后,启动主循环部分,其中使用了两个嵌套的for循环。在内部的循环中,通过检查串口的发送缓冲区状态来发送数据,并在外部的循环中不断增加发送次数并打印信息。
在isr代码中:
主要实现了串口工具对于灯亮灭的操作。并且在串口工具中对接收到的信息进行回复。
在Sent代码中:
实现了在一个无限循环中,定时切换蓝色LED的状态,并通过调试串口打印消息,再通过UART发送一系列字符和字符串。
7.实现UART_2串口的接收程序,当收到字符时:
①在电脑的输出窗口显示下一个字符,如收到A显示B;
②亮灯:收到字符G,亮绿灯;收到字符R,亮红灯;收到字符B,亮蓝灯;收到其他字符,不亮灯。
实现方式:
(1) 用构件调用方式实现;
(2) UART部分用直接地址方式实现(即不调用uart.c中的函数,其他部分如GPIO、中断设置可调用函数)。
(1)用构件方式:
void USART2_IRQHandler(void)
{
uint8_t ch;
uint8_t flag;
DISABLE_INTERRUPTS; //关总中断
if(uart_get_re_int(UART_User))
{
ch=uart_re1(UART_User,&flag);
if(flag)
{
if(ch=='R')
{
gpio_set(LIGHT_GREEN,LIGHT_OFF);
gpio_set(LIGHT_BLUE,LIGHT_OFF);
gpio_set(LIGHT_RED,LIGHT_ON);
}
else if(ch=='G')
{
gpio_set(LIGHT_GREEN,LIGHT_ON);
gpio_set(LIGHT_BLUE,LIGHT_OFF);
gpio_set(LIGHT_RED,LIGHT_OFF);
}
else if(ch=='B')
{
gpio_set(LIGHT_GREEN,LIGHT_OFF);
gpio_set(LIGHT_RED,LIGHT_OFF);
gpio_set(LIGHT_BLUE,LIGHT_ON);
}
else
{
gpio_set(LIGHT_GREEN,LIGHT_OFF);
gpio_set(LIGHT_BLUE,LIGHT_OFF);
gpio_set(LIGHT_RED,LIGHT_OFF);
}
}
}
uart_send1(UART_User,ch+1);
ENABLE_INTERRUPTS; //开总中断
}
(2) UART部分用直接地址方式实现(即不调用uart.c中的函数,其他部分如GPIO、中断设置可调用函数)。
在主函数中添加接收寄存器的定义其他不变
//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发送数据寄存器
volatile uint32_t* uart_rdr; //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基地址
uart_rdr=0x40004424UL; // UART2接收数据寄存器基地址
//(1.2)【不变】关总中断
DISABLE_INTERRUPTS;
//(1.4)给全局变量赋初值
//(1.5)用户外设模块初始化
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));
//配置波特率
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);
//(1.6)使能模块中断
uart_enable_re_int(UART_User); //使能UART_User模块接收中断功能
//(1.7)【不变】开总中断
ENABLE_INTERRUPTS;
for (t = 0; t < 0xFBBB; t++)//查询指定次数
{
//判断接收缓冲区是否满
if (*uart_isr & (0x1UL<<5UL))
{
ch = *uart_rdr; //获取数据,清接收中断位
flag = 1; //接收成功
break;
}
}
if(t >= 0xFBBB)
{
ch = 0xFF;
flag = 0; //未收到数据
}
// 检查是否已经接收到数据
if (flag)
{
// 处理接收到的字符
switch(ch)
{
case 'G':
// 关闭其他颜色LED 打开绿色LED
gpio_set(LIGHT_RED, LIGHT_OFF);
gpio_set(LIGHT_BLUE, LIGHT_OFF);
gpio_set(LIGHT_GREEN, LIGHT_ON);
break;
case 'R':
// 打开红色LED
gpio_set(LIGHT_GREEN, LIGHT_OFF);
gpio_set(LIGHT_BLUE, LIGHT_OFF);
gpio_set(LIGHT_RED, LIGHT_ON);
break;
case 'B':
// 打开蓝色LED
gpio_set(LIGHT_GREEN, LIGHT_OFF);
gpio_set(LIGHT_RED, LIGHT_OFF);
gpio_set(LIGHT_BLUE, LIGHT_ON);
break;
default:
// 关闭所有LED
gpio_set(LIGHT_GREEN, LIGHT_OFF);
gpio_set(LIGHT_RED, LIGHT_OFF);
gpio_set(LIGHT_BLUE, LIGHT_OFF);
break;
}
//发回已接收到的字符
for (t = 0; t < 0xFBBB; t++)//查询指定次数
{
//发送缓冲区为空则发送数据
if ( *uart_isr & (0x1UL<<7UL))
{
*uart_tdr = ch+1;
break;
}
}
}
}
在这个实验中,首先运行和理解了..\04-Software\CH06文件夹中的几个程序,通过阅读这些程序的代码,我对嵌入式系统的基本框架、串口通信、GPIO控制和中断处理有了一定的了解。接着编写UART_2串口的接收程序,使用了调用函数的方式来实现UART的初始化和中断设置,并编写了中断处理函数来处理UART接收到的数据。根据接收到的字符,成功实现了控制绿灯、红灯和蓝灯的亮灭,并在电脑的输出窗口显示下一个字符。在实现过程中,我遇到了一些问题,例如理解中断处理函数的编写方法以及如何正确地控制GPIO以点亮和熄灭LED。通过查阅资料和尝试不同的方法,最终成功地解决了这些问题,并且完成了实验要求,在实验结束后,我意识到嵌入式系统开发中对于细节的重视,以及对硬件和软件交互的深入理解的重要性。
总的来说,在本实验中我熟悉了MCU异步通信Uart的工作原理、通信编程方法、中断的使用方法和串口的调试,对于接下来嵌入式编程有了一定的基础知识。