嵌入式技术基础与实践-第六章学习作业

一、作业1

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

设置GPIOA口,UART2时钟,然后设置对应端口的复用。

配置波特率,过采样因子。开启uart功能,启动串口接收、发送功能。

    //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_RED,GPIO_OUTPUT,LIGHT_OFF);
    gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF);
    gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF);
    
    //使能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); 

2、假设速度115200,系统时钟72MHz,波特率寄存器BRR值

通过这个公式来计算波特率寄存器BRR值

  • 当过采样因子为8:USARTDIV=2*72MHz / 115200 = 1250
  • 当过采样因子为16:USARTDIV=72MHz / 115200 = 625

3、中断向量表在哪个文件中?表中有多少项?

在 startup_stm32l431rctx.s 文件内,具体路径\03_MCU\startup\startup_stm32l431rctx.s

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

寻找中断源TIM6的中断IRQ号为54

IRQn = 54;

((uint32_t)IRQn) >> 5UL //即 54 右移 5位,值为1,即NVIC->ISER[1] 

((uint32_t)IRQn) & 0x1FUL //得到值为22

因此将ISER[1] 的第22位置为1

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

可以发现数据已经是发送成功了,只是没有接收到我们想要的结果

现在在irs.c文件添加TIM6的中断处理函数

可以发现已经可以回显字符。首先可以得到的是UART_2交换后其实是可以正常中断的,但是当我们改变了中断向量表位置和IRQ号后,其执行的对应中断函数也发生了改变,变成了TIM6的中断处理函数。

 二、作业2(同实验)

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

实现方式:

(1) 用构件调用方式实现;

main.c文件

#define GLOBLE_VAR
#include "includes.h"      //包含总头文件

int main(void)
{
    //(1.2)【不变】关总中断
    DISABLE_INTERRUPTS;
    
    //初始化串口模块
    uart_init(UART_User,115200);
    //接受中断使能
    uart_enable_re_int(UART_User);
    
    //(1.5)用户外设模块初始化
    //初始化灯
    gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_OFF);
    gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF);
    gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF); 
    
    //(1.7)【不变】开总中断
    ENABLE_INTERRUPTS;
    
    printf("lxy -- 32106200054");
}

isr.c文件

//=====================================================================
//文件名称:isr.c(中断处理程序源文件)
//框架提供:SD-ARM(sumcu.suda.edu.cn)
//版本更新:20170801-20191020
//功能描述:提供中断处理程序编程框架
//移植规则:【固定】
//=====================================================================
#include "includes.h"
UART_User_Handler(void)
{
    uint8_t ch;
    uint8_t flag;
    //关中断
    DISABLE_INTERRUPTS;
    //读取一个字节
    ch = uart_re1(UART_User, &flag);
    //判断是否接收到一个字节
    if(flag)
    {
        if(ch == 'G')
        {
            //发送下一个字符
            uart_send1(UART_User, (ch+1));
            //
            uart_send_string(UART_User,(uint8_t*)"LXY 亮绿灯");
            
            gpio_set(LIGHT_RED, LIGHT_OFF);
            gpio_set(LIGHT_BLUE, LIGHT_OFF);
            gpio_set(LIGHT_GREEN, LIGHT_ON); 
        }
        else if(ch == 'R')
        {
            //发送下一个字符
            uart_send1(UART_User, (ch+1));
            //
            uart_send_string(UART_User,(uint8_t*)"LXY 亮红灯");
            
            gpio_set(LIGHT_GREEN, LIGHT_OFF);
            gpio_set(LIGHT_BLUE, LIGHT_OFF);
            gpio_set(LIGHT_RED, LIGHT_ON);
        }
        else if(ch == 'B')
        {
            //发送下一个字符
            uart_send1(UART_User, (ch+1));
            //
            uart_send_string(UART_User,(uint8_t*)" LXY 亮蓝灯");
            
            gpio_set(LIGHT_RED, LIGHT_OFF);
            gpio_set(LIGHT_GREEN, LIGHT_OFF);
            gpio_set(LIGHT_BLUE, LIGHT_ON);
        }
        else
        {
            //发送下一个字符
            uart_send1(UART_User, (ch+1));
            //不亮灯
            gpio_set(LIGHT_RED, LIGHT_OFF);
            gpio_set(LIGHT_GREEN, LIGHT_OFF);
            gpio_set(LIGHT_BLUE, LIGHT_OFF);
        }
    }
    //开中断
    ENABLE_INTERRUPTS;
}

调用构件方式还是非常方便快捷的。

实验的结果

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

main.c文件

#define GLOBLE_VAR
#include "includes.h"      //包含总头文件

//----------------------------------------------------------------------
//声明使用到的内部函数
//main.c使用的内部函数声明处

//----------------------------------------------------------------------
//主函数,一般情况下可以认为程序从此开始运行(实际上有启动过程,参见书稿)
int main(void)
{
    //(1)======启动部分(开头)==========================================
    //(1.1)声明main函数使用的局部变量
    uint8_t  mTest;
    uint32_t mCount;
    
    //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_RED,GPIO_OUTPUT,LIGHT_OFF);
    gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_OFF);
    gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_OFF);
    
    //使能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); 
     
    //开放uart接收中断
    *uart_cr1 |= (0x1UL<<5U);
    //开中断控制器IRQ中断
    NVIC_EnableIRQ(USART2_IRQn);
    
    //(1.7)【不变】开总中断
    ENABLE_INTERRUPTS;
    
    printf("lxy -- 32106200054\n");
}

isr.c文件

//=====================================================================
//文件名称:isr.c(中断处理程序源文件)
//框架提供:SD-ARM(sumcu.suda.edu.cn)
//版本更新:20170801-20191020
//功能描述:提供中断处理程序编程框架
//移植规则:【固定】
//=====================================================================
#include "includes.h"
#define GLOBLE_VAR

volatile uint32_t* uart_isr=(volatile uint32_t*)0x4000441CUL;         // UART中断和状态寄存器基地址
volatile uint32_t* uart_tdr=(volatile uint32_t*)0x40004428UL;         //UART发送数据寄存器
volatile uint32_t* uart_rdr=(volatile uint32_t*)0x40004424UL;         //UART接收数据寄存器

void User_SysFun(uint8_t ch);
//声明一个发送字符串的函数
void Uart_Send_String(uint8_t* sendMsg);
uint8_t Uart_Send1(uint8_t ch);

void USART2_IRQHandler(void)
{
    uint8_t ch;
    uint8_t flag;
    uint32_t t;
    uint8_t* sendMsg = "";
    //关中断
    DISABLE_INTERRUPTS;
    //读取一个字节
    //ch = uart_re1(UART_User, &flag);
    for(t = 0; t < 0xFBBB; t++)//查询指定次数
    {
        //判断接收缓冲区是否满
        if((*uart_isr) & (1<<5U))
        {
            ch = *uart_rdr;
            flag = 1;
            *uart_isr &= ~(1<<5U);//第五位清零
            break;
        }
    }
    if(t >= 0xFBBB)
	{
		ch = 0xFF;
		flag = 0;    //未收到数据
	}
	
    //判断是否接收到一个字节
    if(flag)
    {
        //发送下一个字符
        //uart_send1(UART_User, (ch+1));
        Uart_Send_String("刘信扬");
        Uart_Send1(ch+1);
        //不亮灯
        gpio_set(LIGHT_RED, LIGHT_OFF);
        gpio_set(LIGHT_GREEN, LIGHT_OFF);
        gpio_set(LIGHT_BLUE, LIGHT_OFF);
        if(ch == 'G')
        {
            //uart_send_string(UART_User,(uint8_t*)"LXY 亮绿灯");
            Uart_Send_String((uint8_t*)"亮绿灯");
            gpio_set(LIGHT_RED, LIGHT_OFF);
            gpio_set(LIGHT_BLUE, LIGHT_OFF);
            gpio_set(LIGHT_GREEN, LIGHT_ON); 
        }
        else if(ch == 'R')
        {
            //uart_send_string(UART_User,(uint8_t*)"LXY 亮红灯");
            Uart_Send_String((uint8_t*)"亮红灯");
            gpio_set(LIGHT_GREEN, LIGHT_OFF);
            gpio_set(LIGHT_BLUE, LIGHT_OFF);
            gpio_set(LIGHT_RED, LIGHT_ON);
        }
        else if(ch == 'B')
        {
            //uart_send_string(UART_User,(uint8_t*)" LXY 亮蓝灯");
           Uart_Send_String((uint8_t*)"亮蓝灯");
            gpio_set(LIGHT_RED, LIGHT_OFF);
            gpio_set(LIGHT_GREEN, LIGHT_OFF);
            gpio_set(LIGHT_BLUE, LIGHT_ON);
        }
    }
    //开中断
    ENABLE_INTERRUPTS;
}

//定义
void Uart_Send_String(uint8_t* sendMsg)
{
    uint16_t i = 0;
    uint8_t *p = (uint8_t *)sendMsg;     //定义指针指向要发送字符串首地址
    
    for(i = 0; p[i] != '\0'; i++)   //遍历字符串里的字符
    {
         //发送指针对应的字符
         if(!Uart_Send1(p[i]))
         {
             break;
         }
    }
}
uint8_t Uart_Send1(uint8_t ch)
{
    uint32_t t;
    for (t = 0; t < 0xFBBB; t++)//查询指定次数
	{
	    //发送缓冲区为空则发送数据
		if ((*uart_isr) & (1<<7U))
		{
		    *uart_tdr = ch;
			 break;
		}
	}
	if (t >= 0xFBBB)
	    return 0; //发送超时,发送失败
	else
	    return 1;
}

//内部函数
void User_SysFun(uint8_t ch)
{
    //(1)收到的一个字节参与组帧
    if(gcRecvLen == 0)  gcRecvLen =useremuart_frame(ch,(uint8_t*)gcRecvBuf);
    //(2)字节进入组帧后,判断gcRecvLen=0?若为0,表示组帧尚未完成,
    //     下次收到一个字节,再继续组帧
    if(gcRecvLen == 0) goto User_SysFun_Exit;
    //(3)至此,gcRecvLen≠0,表示组帧完成,gcRecvLen为帧的长度,校验序列号后(与
    //     根据Flash中倒数一扇区开始的16字节进行比较)
    //     gcRecvBuf[16]进行跳转
    if(strncmp((char *)(gcRecvBuf),(char *)((MCU_SECTOR_NUM-1)*MCU_SECTORSIZE+
       MCU_FLASH_ADDR_START),16) != 0)
    {
        gcRecvLen = 0;         //恢复接收状态
        goto User_SysFun_Exit;
    }
    //(4)至此,不仅收到完整帧,且序号比较也一致, 根据命令字节gcRecvBuf[16]进行跳转
    //若为User串口程序更新命令,则进行程序更新
    switch(gcRecvBuf[16])  //帧标识
    {
        case 0:
            SYSTEM_FUNCTION((uint8_t *)(gcRecvBuf+17));
            gcRecvLen = 0;         //恢复接收状态
        break;
        default:
        break;
    }
User_SysFun_Exit:
    return;
}

实验结果:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值