嵌入式作业(5)

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

//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寄存器应赋的值

    //添加此寄存器地址

    volatile uint32_t* uart_rdr;

    //变量赋值

    uart_rdr=0x40004424UL;

    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基地址

 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)使能模块中断

    uart_enable_re_int(UART_User);   ENABLE_INTERRUPTS;

        

2、假设速度为115200,系统时钟为72MHz,波特率寄存器BRR中的值应该是多少?

通过代码

    if(*uart_cr1&(0x1UL<<15) == (0x1UL<<15))             
    usartdiv = (uint16_t)((SystemCoreClock/115200)*2);
    else
    usartdiv = (uint16_t)((SystemCoreClock/115200));
    *uart_brr = usartdiv

有两种情况,可能为1250,可能为625.

3、中断向量表在哪个文件中?表中有多少项?给出部分截图。

一共有98项。


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

利用函数__STATIC_INLINE void __NVIC_EnableIRQ(IRQn_Type IRQn)实例化。

各项具体数值:

刚刚看到,传入的IRQn将是54,通过函数的if语句,结合其左右移操作等,得到的返回值:

return((uint32_t)(((NVIC->ISER[(((uint32_t)IRQn) >> 5UL)] & (1UL << (((uint32_t)IRQn) & 0x1FUL))) != 0UL) ? 1UL : 0UL));

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

理论上来说,更换位置和IRQ号,并不会影响其中断的正常,中断向量号通过初始化从篇后,映射到IRQ中,只要对应的IRQ号对应的中断处理程序,没问题就不会出错。

  • 程序源码

我将实验步骤也放在这里:

准备导入工程:

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

说明:

对涉及的函数进行说明:

Uart_init 传入串口号及波特率 初始化串口

使用串口前,我们将使用该函数进行对串口模块的初始化。

Uart_send1 对指定的串口发送1字节数据

Uart_send_string 向指定的串口发送字符串。

gpio_init 初始化灯的参数。

uart_enable_re_int 使某个模块接收中断功能。

特殊说明:

在程序中将会使用 UART_User 此宏,代表用户串口 UART_2

以及UART_USER_handler 指的是我们即将实现的MZL_USART2_IRQHANDLET

这里在前面加上我名字的前缀。

我将urat2串口的中断处理程序也放在了main.c文件中(即实现收到字符打印下一个字符或亮灯的函数操作)。Main函数进行各种初始化。

Main.c代码:
#define GLOBLE_VAR

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

 void MZL_USART2_IRQHandler(void)

{

uint8_t ch;

uint8_t flag;

gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_ON);

gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_ON);

gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_ON);

flag =0;

DISABLE_INTERRUPTS;   

ch=uart_re1(UART_User,&flag);  //flag将由cpu赋值 若flag的值不为0 即有数据

if(flag)

{

     if(ch=='R')

      {

gpio_set(LIGHT_RED,LIGHT_ON);

     gpio_set(LIGHT_GREEN,LIGHT_OFF);

     gpio_set(LIGHT_BLUE,LIGHT_OFF);

     uart_send_string(UART_User,(uint8_t *)"receive the character:");

     uart_send1(UART_User,ch);

     uart_send_string(UART_User,(uint8_t *)",the Red light is on :");

     uart_send1(UART_User,ch+1);

     uart_send_string(UART_User,(uint8_t *)"   ");

        }

        else if(ch=='G')

        {

gpio_set(LIGHT_RED,LIGHT_OFF);

     gpio_set(LIGHT_GREEN,LIGHT_ON);

     gpio_set(LIGHT_BLUE,LIGHT_OFF);

     uart_send_string(UART_User,(uint8_t *)"receive the character:");

     uart_send1(UART_User,ch);

     uart_send_string(UART_User,(uint8_t *)",the Green light is on :");

     uart_send1(UART_User,ch+1);

     uart_send_string(UART_User,(uint8_t *)"   ");

        }

        else if(ch=='B')

        {

gpio_set(LIGHT_RED,LIGHT_OFF);

     gpio_set(LIGHT_GREEN,LIGHT_OFF);

     gpio_set(LIGHT_BLUE,LIGHT_ON);

     uart_send_string(UART_User,(uint8_t *)"receive the character:");

     uart_send1(UART_User,ch);

     uart_send_string(UART_User,(uint8_t *)",the Blue light is on :");

     uart_send1(UART_User,ch+1);

     uart_send_string(UART_User,(uint8_t *)"   ");

        }

        else

        {

     gpio_set(LIGHT_RED,LIGHT_OFF);

     gpio_set(LIGHT_GREEN,LIGHT_OFF);

    gpio_set(LIGHT_BLUE,LIGHT_OFF);

     uart_send_string(UART_User,(uint8_t *)"had received the character:");

     uart_send1(UART_User,ch);

     uart_send_string(UART_User,(uint8_t *)"the next character is :");

     uart_send1(UART_User,ch+1);

     uart_send_string(UART_User,(uint8_t *)"   ");

        }

}

ENABLE_INTERRUPTS;    //开总中断

 }

int main(void)

{

uint32_t mMainLoopCount =0 ;  

DISABLE_INTERRUPTS;//为保证以下的初始化操作不被打断 暂时关闭中断

uart_init(UART_User,115200);

uart_enable_re_int(UART_User);  //使能UART_User模块接收中断功能

ENABLE_INTERRUPTS;

printf("红发阿龙的实验:\n Enter the next character , I will display the next character!!!!!!!!!\n");

while(mMainLoopCount<=200000000){

mMainLoopCount++;

if(mMainLoopCount==200000000)mMainLoopCount=0;

}

}   

五、运行结果

编译工程:
 

进行串口更新:

接下来打开串口工具,发送对应的数据到串口中,发起中断,使得对应的中断处理程序进行反应。

输入字符没反应,后面才知道,是因为这里选择错了。

回到串口更新看芯片链接的是哪个串口。

是COM12;

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

由于main.c文件里,并没有UART发送数据寄存器,所以要添加。

由于我们进行的是直接地址方式实现,我们在中断处理程序里,不能调用uart.c等文件的函数,所以要获得我们的对应的串口地址如UART发送数据寄存器,UART_接收数据寄存器等地址。

另外,在USART2_IRQHandler函数里,地址使用uint8_t的目的是,选择此指针为8位的跳跃力。

并且,通过usar_isr寄存器进行判断,第五位检测是否有数据,若有数据,根据该字符进行对应的操作,并回发该字符的下一个字符,即ch+1。

更新成功。

发送字符操作:

代码:

Mian.c:

#define GLOBLE_VAR

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

int main(void)

{

    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寄存器应赋的值

    

    //添加此寄存器地址

    volatile uint32_t* uart_rdr;

    //变量赋值

    uart_rdr=0x40004424UL;

    

    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)使能模块中断

    uart_enable_re_int(UART_User);   ENABLE_INTERRUPTS;

        

  printf("MZL 实验二 !!!") ;

  while(1);

}    

    

 Isr.c文件:

#include "includes.h"

//内部函数

void User_SysFun(uint8_t ch)

{

    if(gcRecvLen == 0)  gcRecvLen =useremuart_frame(ch,(uint8_t*)gcRecvBuf);

    if(gcRecvLen == 0) goto User_SysFun_Exit;

    if(strncmp((char *)(gcRecvBuf),(char *)((MCU_SECTOR_NUM-1)*MCU_SECTORSIZE+

       MCU_FLASH_ADDR_START),16) != 0)

    {

        gcRecvLen = 0;         //恢复接收状态

        goto User_SysFun_Exit;

    }

    switch(gcRecvBuf[16])  //帧标识

    {

        case 0:

            SYSTEM_FUNCTION((uint8_t *)(gcRecvBuf+17));

            gcRecvLen = 0;         //恢复接收状态

        break;

        default:

        break;

    }

User_SysFun_Exit:

    return;

}

void USART2_IRQHandler(void)

{

uint8_t ch;

uint8_t flag;

volatile uint8_t *uart_isr=0x4000441CUL;

volatile uint8_t *uart_rdr=0x40004424UL;

volatile uint8_t *uart_tdr=0x40004428UL;  

gpio_init(LIGHT_BLUE,GPIO_OUTPUT,LIGHT_ON);

gpio_init(LIGHT_GREEN,GPIO_OUTPUT,LIGHT_ON);

gpio_init(LIGHT_RED,GPIO_OUTPUT,LIGHT_ON); //这里里一共 uint8_t 目的是选择此指针为8位的跳跃力

DISABLE_INTERRUPTS;  

//接收一个字节的数据

if (*uart_isr & (0x1UL<<5UL))

    {

      ch = *uart_rdr;

      flag=1;

     }

if(flag)                       //有数据

{

     if(ch=='R')

      {

         gpio_set(LIGHT_RED,LIGHT_ON);

     gpio_set(LIGHT_GREEN,LIGHT_OFF);

     gpio_set(LIGHT_BLUE,LIGHT_OFF);

        }

        else if(ch=='G')

        {

            gpio_set(LIGHT_RED,LIGHT_OFF);

     gpio_set(LIGHT_GREEN,LIGHT_ON);

     gpio_set(LIGHT_BLUE,LIGHT_OFF);

        }

        else if(ch=='B')

        {

            gpio_set(LIGHT_RED,LIGHT_OFF);

     gpio_set(LIGHT_GREEN,LIGHT_OFF);

     gpio_set(LIGHT_BLUE,LIGHT_ON);

        }

        else

        {

            gpio_set(LIGHT_RED,LIGHT_OFF);

     gpio_set(LIGHT_GREEN,LIGHT_OFF);

     gpio_set(LIGHT_BLUE,LIGHT_OFF);

         }

             *uart_tdr = ch+1; //发送下一个字符

        

}

ENABLE_INTERRUPTS;

  }

  • 分析思考
    (1)此实验给我的感悟就是,嵌入式和操作系统内核的编写都属于难度比较大的开发,由于牵涉到很多串口,底层的原理,所以不像机器学习还有算法等,只需要懂原理还有思想即可,但嵌入式开发等,都需要巨大的知识的储备,如各种调用约定,地址的设定,当我点开看到整个工程文件,我深刻理解到这是一个完整的微型操作系统,而本实验仅仅是编写了其中一个中断处理程序,由于上学期我学过如何编写操作系统内核,即操作系统真相还原,故理解起来还比较轻松,所以学习课程的一大益处即是,能够让你的学习能力进一步提升,而不仅仅只是知识层面的提升。

(2)知识要素:

 1.本文件中的中断处理函数调用的均是相关设备封装好的具体构件,在更换芯片

 时,只需保证设备的构件接口一致,即可保证本文件的相关中断处理函数不做任何

 更改,从而达到芯片无关性的要求。

这个在原工程文件就有的一段注释我觉得是精华,我们开发的一个很重要的一个思考的地方即 -- 移植性,如果你对一个硬件就要开发一种软件,可想而知,世界上该有多少软件,而这里由于封装的问题,使得我们如果更换别的芯片时,只需要保证构建结构一致即可,达到了芯片无关性。这让我想起了操作系统的一个重要思想,即设备无关性。

  • 21
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
XJTU嵌入式作业2要求实现一个简单的温度转换程序。该程序需要输入一个摄氏温度值,并将其转换为华氏温度和开氏温度后输出。 为了完成这个任务,我们首先需要使用C语言编写一个能够进行温度转换的函数。该函数可以接受用户输入的摄氏温度值,并根据转换公式将其转换为华氏温度和开氏温度。 转换摄氏温度到华氏温度的公式如下: 华氏温度 = 摄氏温度 * 9/5 + 32 转换摄氏温度到开氏温度的公式如下: 开氏温度 = 摄氏温度 + 273.15 在程序中,我们可以使用scanf函数来接受用户输入的摄氏温度值。然后,我们可以声明并初始化华氏温度和开氏温度的变量,并根据转换公式进行计算。 最后,我们可以使用printf函数来输出转换后的华氏温度和开氏温度值。 除了实现温度转换的函数外,我们还可以编写一个主函数来测试该函数的正确性。在主函数中,我们可以调用温度转换函数,并将用户输入的摄氏温度值作为参数传递给该函数。 在运行程序时,用户将被要求输入一个摄氏温度值。程序将自动将该温度转换为华氏温度和开氏温度,并将转换结果输出到屏幕上。 总而言之,XJTU嵌入式作业2要求我们通过C语言编写一个温度转换程序。该程序可以将摄氏温度转换为华氏温度和开氏温度,并将转换结果输出到屏幕上。该程序包括一个温度转换函数和一个主函数来测试该函数的正确性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值