实现UART_2串口的接收程序,当收到字符时:
①在电脑的输出窗口显示下一个字符,如收到A显示B;
②亮灯:收到字符G,亮绿灯;收到字符R,亮红灯;收到字符B,亮蓝灯;收到其他字符,不亮灯。
实现方式:
一、 用构件调用方式实现;
部分代码:
Isr.c:
void USART2_IRQHandler(void)
{
uint8_t ch;
uint8_t flag;
DISABLE_INTERRUPTS; // 关总中断
// 接收一个字节的数据
ch = uart_re1(UART_User, &flag); // 调用接收一个字节的函数,清接收中断位
if(flag) // 如果有数据
{
// 在电脑的输出窗口显示下一个字符
uart_send1(UART_User, ch + 1); // 回发接收到的字节的下一个字符
// 控制灯的亮灭
switch(ch)
{
case 'G':
// 点亮绿灯
gpio_set(LIGHT_GREEN,LIGHT_ON);
gpio_set(LIGHT_RED,LIGHT_OFF);
gpio_set(LIGHT_BLUE,LIGHT_OFF);
break;
case 'R':
// 点亮红灯
gpio_set(LIGHT_RED,LIGHT_ON);
gpio_set(LIGHT_GREEN,LIGHT_OFF);
gpio_set(LIGHT_BLUE,LIGHT_OFF);
break;
case 'B':
// 点亮蓝灯
gpio_set(LIGHT_BLUE,LIGHT_ON);
gpio_set(LIGHT_RED,LIGHT_OFF);
gpio_set(LIGHT_GREEN,LIGHT_OFF);
break;
default:
// 关闭所有灯
gpio_set(LIGHT_GREEN,LIGHT_OFF);
gpio_set(LIGHT_RED,LIGHT_OFF);
gpio_set(LIGHT_BLUE,LIGHT_OFF);
break;
}
}
修改main.c:(加上定义)
gpio_init(LIGHT_GREEN, GPIO_OUTPUT, LIGHT_OFF);
gpio_init(LIGHT_RED, GPIO_OUTPUT, LIGHT_OFF);
gpio_init(LIGHT_BLUE, GPIO_OUTPUT, LIGHT_OFF);
运行截图:
红灯
蓝灯
绿灯
输入字母“A”
输入数字6
二、UART部分用直接地址方式实现(即不调用uart.c中的函数,其他部分如GPIO、中断设置可调用函数)。
修改代码
isr.c:
//uart寄存器相关地址
volatile uint32_t* uart_isr=0x4000441CUL; // UART中断和状态寄存器基地址
volatile uint32_t* uart_tdr=0x40004428UL; // UART发送数据寄存器
volatile uint32_t* uart_rdr=0x40004424UL; // UART接收数据寄存器
void USART2_IRQHandler(void)
{
uint8_t ch;
DISABLE_INTERRUPTS; // 关总中断
// 检查接收数据寄存器是否非空
if (USART2->ISR & USART_ISR_RXNE_Msk)
{
// 读取接收到的数据
ch = USART2->RDR;
// 在电脑的输出窗口显示下一个字符
// USART 发送数据
// 等待发送缓冲区为空
while (!(USART2->ISR & USART_ISR_TXE_Msk))
;
// 将数据写入发送数据寄存器
USART2->TDR = ch + 1; // 回发接收到的字节的下一个字符
// 控制灯的亮灭
// 控制灯的亮灭
switch(ch)
{
case 'G':
// 点亮绿灯
gpio_set(LIGHT_GREEN,LIGHT_ON);
gpio_set(LIGHT_RED,LIGHT_OFF);
gpio_set(LIGHT_BLUE,LIGHT_OFF);
break;
case 'R':
// 点亮红灯
gpio_set(LIGHT_RED,LIGHT_ON);
gpio_set(LIGHT_GREEN,LIGHT_OFF);
gpio_set(LIGHT_BLUE,LIGHT_OFF);
break;
case 'B':
// 点亮蓝灯
gpio_set(LIGHT_BLUE,LIGHT_ON);
gpio_set(LIGHT_RED,LIGHT_OFF);
gpio_set(LIGHT_GREEN,LIGHT_OFF);
break;
default:
// 关闭所有灯
gpio_set(LIGHT_GREEN,LIGHT_OFF);
gpio_set(LIGHT_RED,LIGHT_OFF);
gpio_set(LIGHT_BLUE,LIGHT_OFF);
break;
}
}
ENABLE_INTERRUPTS; // 开总中断
}
main.c:
#define GLOBLE_VAR
#include "includes.h" //包含总头文件
extern USART_TypeDef *USART_ARR[];
int main(void)
{
//(1)======启动部分(开头)==========================================
//(1.1)声明main函数使用的局部变量
uint8_t mTest;
uint32_t mCount;
gpio_init(LIGHT_GREEN, GPIO_OUTPUT, LIGHT_OFF);
gpio_init(LIGHT_RED, GPIO_OUTPUT, LIGHT_OFF);
gpio_init(LIGHT_BLUE, GPIO_OUTPUT, LIGHT_OFF);
//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基地址
//(1.2)【不变】关总中断
DISABLE_INTERRUPTS;
//(1.3)给主函数使用的局部变量赋初值
mCount=0;
//(1.4)给全局变量赋初值
//(1.5)用户外设模块初始化
//gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_ON); //初始化蓝灯
//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)使能模块中断
USART_ARR[1]->CR1 |= USART_CR1_RXNEIE_Msk;//使能 USART2 的接收中断
NVIC_EnableIRQ(USART2_IRQn);//USART2_IRQn 是 USART2 的中断号,在这里被传递给 NVIC_EnableIRQ 函数,从而使得系统能够响应 USART2 的中断。
//(1.7)【不变】开总中断
ENABLE_INTERRUPTS;
printf("LYY_32106100020%d\r\n",mCount);
printf("“工具”→“串口工具”,打开接收User串口数据观察\r\n");
}
运行截图:
红灯
蓝灯
绿灯
输入A32106100020LYY
分析与思考:
一、用构件调用方式实现;
调用了uart.c中的函数:
ch = uart_re1(UART_User, &flag);
uart_re1 函数用于从指定的串口(这里是 UART_User)接收一个字节的数据,并将接收到的字节存储在 ch 变量中。接收一个指向串口结构体的指针和一个指向标志位的指针。标志位通常用于指示是否接收到了数据。在函数内部,检查串口的接收缓冲区是否有数据可读,如果有,就从中读取一个字节并返回。
uart_send1(UART_User, ch + 1);
uart_send1 函数用于向指定的串口发送一个字节的数据,数据为 ch + 1。接收一个指向串口结构体的指针和要发送的数据。在函数内部,将数据写入串口的发送缓冲区,并触发发送中断,以便将数据发送出去。
uart_enable_re_int(UART_User);
一个假设的函数调用,用于启用指定串口(UART_User)的接收中断。一旦启用了接收中断,当串口接收到数据时,将触发中断服务程序(ISR)来处理接收到的数据。在使用串口进行通信时,通常会启用接收中断来实现异步接收数据的功能,这样可以在主程序的其余部分执行其他任务,而不必一直等待接收到数据。
二、UART部分用直接地址方式实现
1、申明地址变量
2、给地址变量赋值
3、UART初始化:设置引脚复用功能为串口。设置波特率。开启UART功能。
4、修改isr.c:
if (USART2->ISR & USART_ISR_RXNE_Msk):检查 USART2 接收寄存器是否有数据可读。如果接收到了数据,USART_ISR_RXNE_Msk 位会被置位。
ch = USART2->RDR;:从接收数据寄存器 (RDR) 中读取接收到的字符,并将其保存在变量 ch 中。
while (!(USART2->ISR & USART_ISR_TXE_Msk)) ;:等待发送缓冲区为空。当发送缓冲区为空时,USART_ISR_TXE_Msk 位会被置位。这个循环会一直等待,直到发送缓冲区为空。
USART2->TDR = ch + 1;:将接收到的字符加一后发送。这里做了一个简单的处理,将接收到的字符加一后发送回去。数据被写入发送数据寄存器 (TDR),然后 USART2 会将其发送出去。
5、修改main.c:
uart_enable_re_int(UART_User); 改用为:
USART_ARR[1]->CR1 |= USART_CR1_RXNEIE_Msk;
NVIC_EnableIRQ(USART2_IRQn);
USART_ARR[1]->CR1 |= USART_CR1_RXNEIE_Msk;:使能 USART2 的接收中断。首先,USART_ARR[1] 表示取 USART_ARR 数组中索引为 1 的元素,即指向 USART2 寄存器的指针。然后,使用箭头操作符(->)来访问该寄存器的 CR1 寄存器,并将其的 RXNEIE 位设置为 1,从而使能接收中断。
NVIC_EnableIRQ(USART2_IRQn);:使能 USART2 的中断线。USART2_IRQn 是 USART2 的中断号,在这里被传递给 NVIC_EnableIRQ 函数,从而使得系统能够响应 USART2 的中断。
三、感想
改uart_enable_re_int(UART_User);第一次改成UART_2->CR1 |= USART_CR1_RXNEIE_Msk;
结果报错:没有定义UART_2类型后来查阅uart.c看到语句:
USART_TypeDef *USART_ARR[] = {(USART_TypeDef*)USART1_BASE, (USART_TypeDef*)USART2_BASE, (USART_TypeDef*)USART3_BASE};
才把原来的语句改成
USART_ARR[1]->CR1 |= USART_CR1_RXNEIE_Msk;
USART_TypeDef 是 UART 寄存器结构体的类型,而 USART_ARR 是一个指针数组,包含了指向不同 UART 寄存器的指针。USART_ARR[1] 表示数组中第二个元素,指向 USART2 寄存器的指针。就可以使用箭头操作符(->)来访问该寄存器的成员,并对其进行操作。