嵌入式系统学习第六章(二)

实现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 寄存器的指针。就可以使用箭头操作符(->)来访问该寄存器的成员,并对其进行操作。

  • 24
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值