PotatoPie 4.0 实验教程(41) —— FPGA实现RISC-V 扩展 GPIO UART Timer功能

TD工程介绍

我们提供的TD工程里的RISC-V核默认就开启了GPIO UART扩展,可以看到还有SPI和I2C扩展。因此后面的实验中TD的工程我们基本不怎么修改TD的内容,只需要修改TD工具中Soc_Top.v文件中的TCM0_INITFILE为FD生成的固件名称即可,主要修我以为都是在FD工程进行修改即可。

FD工程介绍

这个工程我们主要演示了如何使用UART及UART中断,如何使用定时器中断,以及如何使用GPIO和GPIO中断。

FD工程导入

打开FD选择workspace为 demo_riscv\wkspace 就会自动导入该workspace下的所有项目。

由于你的FD的目录与我的FD目录不在同一个位置,会导致编译器工具链的找不着,我们需要在工程上右键选择Reset Project Toolchain

执行完这个操作就FD会重新修正工程的工具链配置。

FD工程源代码分析

头文件、宏定义及全局变量介绍

头文件

 

#include "core.h" // 核心头文件

//#define USE_MTIME // 不使用MTIME

#include "uart.h" // UART 头文件

#include "gpio.h" // GPIO 头文件

#include "anl_printf.h" // 打印库头文件

#include "interrupt.h" // 中断库头文件

#ifdef USE_MTIME

#include "mtime.h" // 如果使用 MTIME,则引入 MTIME 头文件

#else

#include "systick.h" // 否则引入 SysTick 头文件

#endif

  1. #include "core.h":这个头文件是系统的核心头文件,通常包含了与硬件相关的低级别配置和宏定义。它可能包含了处理器寄存器的定义、中断控制器配置等内容。

  2. #include "uart.h":这个头文件包含了 UART(通用异步收发传输)相关的函数声明和宏定义。

  3. #include "gpio.h":这个头文件包含了 GPIO(通用输入输出)相关的函数声明和宏定义。

  4. #include "anl_printf.h":这个头文件包含了打印函数的声明和定义。

  5. #include "interrupt.h":这个头文件包含了中断控制器相关的函数声明和宏定义。

  6. #ifdef USE_MTIME:这是一个条件编译指令,用于根据是否定义了 USE_MTIME 宏来选择性地包含不同的头文件内容。

  7. #include "mtime.h":如果定义了 USE_MTIME 宏,就会包含这个头文件。它可能包含了 MTIME(Machine Timer)相关的函数声明和宏定义,用于配置和操作处理器的计时器。

  8. #include "systick.h":如果没有定义 USE_MTIME 宏,就会包含这个头文件。它可能包含了 SysTick 定时器相关的函数声明和宏定义,用于配置和操作 SysTick 定时器。

  9. Systick 和 MTIME 定时器的区别主要是其资源占用和操作模式。 Systick 是一个自动重装的 30bit 定时器,用户需要使用 SetSystickCfg()函数启用定时器,并在定时器中断处理程序中使用 ClrSystickInt()函数清除掉挂起的定时器状态对于 MTIME 定时器而言,它的初始化和中断响应都是使用 SetMtimeCmp()函数将下一时钟节拍的计数值装入定时器比较值中。

全局变量sys_banner

static char sys_banner[] = {"- Anlogic eMCU buildtime [" __TIME__" " __DATE__ "] " "rev 1.0 \r\n"};

这行代码定义了一个静态字符数组 sys_banner,用于存储系统的横幅信息。该信息包含了编译时间和日期,以及版本号。

  • __TIME__:编译时的时间,格式为 HH:MM:SS。
  • __DATE__:编译时的日期,格式为 MMM DD YYYY(月份、日期、年份)。

这两个宏是编译器提供的预定义宏,在编译时会被替换为当前的编译时间和日期。在这里,它们被用于构建一个包含编译时间和日期的字符串。

宏定义

 

//THIS DEMO IS FOR SYSTICK SYSTEM

#define SYS_FREQ 80000000 // 系统频率

#define UART_BAUD 115200 // UART 波特率

#define GPIO_INTSRC 0x04 // GPIO 中断源

#define UART1_INTSRC 0x01 // UART1 中断源

#define TIM_TRIG_FREQ 1 // 定时器触发频率,每秒1次

这段代码是一些预定义的常量和注释,用于描述程序的一些基本参数和特性:

  1. // THIS DEMO IS FOR SYSTICK SYSTEM:这是一个注释,用于说明这段代码是针对 SysTick 系统设计的演示程序。

  2. #define SYS_FREQ 80000000:这是一个宏定义,表示系统的频率为 80MHz。这个频率用于配置定时器、UART 通信等时序相关的功能。

  3. #define UART_BAUD 115200:这是一个宏定义,表示 UART 的波特率为 115200。波特率是串行通信中表示数据传输速率的参数。

  4. #define GPIO_INTSRC 0x04:这是一个宏定义,表示 GPIO 中断源的值为 0x04。在某些系统中,GPIO 的中断可以被多个源触发,此处定义了其中一个源的标识值。

  5. #define UART1_INTSRC 0x01:这是一个宏定义,表示 UART1 中断源的值为 0x01。类似于上面的 GPIO 中断源,这里定义了 UART1 中断的标识值。

  6. #define TIM_TRIG_FREQ 1:这是一个宏定义,表示定时器的触发频率为每秒 1 次。这个参数用于配置定时器中断的触发频率,可以根据需要进行调整。

函数说明

main()函数

  • 先配置UART
 

// 配置 UART 参数

Uart_Config UART1_Cfg;

UART1_Cfg.BaudDivider=(SYS_FREQ/UART_BAUD);

UART1_Cfg.IntEnable=True;

UART1_Cfg.Event_ParityCheckFail=False;

UART1_Cfg.Event_RxFifoHalfFull=False;

UART1_Cfg.Event_TxFifoHalfEmpty=False;

UART1_Cfg.Event_RxBufFull=True;

UART1_Cfg.Event_TxBufEmpty=False;

UART1_Cfg.RxFifoEnable=False;

UART1_Cfg.TxFifoEnable=True;

UART1_Cfg.Parity=NONE;

UART1_Cfg.Stop=ONE;

uart_applyConfig(UART,&UART1_Cfg);

这段代码是配置 UART 参数的过程,具体的配置包括:

  • BaudDivider:波特率分频器,根据系统时钟频率 SYS_FREQ 和波特率 UART_BAUD 计算得出。
  • IntEnable:使能 UART 中断。
  • Event_ParityCheckFail:奇偶校验失败事件的处理,设置为 False 表示不处理。
  • Event_RxFifoHalfFull:接收 FIFO 缓冲区半满事件的处理,设置为 False 表示不处理。
  • Event_TxFifoHalfEmpty:发送 FIFO 缓冲区半空事件的处理,设置为 False 表示不处理。
  • Event_RxBufFull:接收缓冲区满事件的处理,设置为 True 表示处理。
  • Event_TxBufEmpty:发送缓冲区空事件的处理,设置为 False 表示不处理。
  • RxFifoEnable:使能接收 FIFO 缓冲区,设置为 False 表示不使用 FIFO 缓冲区。
  • TxFifoEnable:使能发送 FIFO 缓冲区,设置为 True 表示使用 FIFO 缓冲区。
  • Parity:奇偶校验类型,设置为 NONE 表示不使用奇偶校验。
  • Stop:停止位数,设置为 ONE 表示一个停止位。

最后,通过 uart_applyConfig 函数将以上配置应用到 UART 上。

  • 配置完后打印系统信息
 

// 打印系统信息

anl_printf("---------------------------------------------------------------- \r\n");

anl_printf("- SoftCore Demo : RISCV(Freq 80MHz. 32kB RAM. Full Feature) \r\n");

anl_printf("- Hardware Platform : EG4S20NG88 PotatoPie V4.0 \r\n");

anl_printf("- TD Ver. : TD 5.6.4(97693) \r\n");

anl_printf("- OS+Build+Debug : Windows, OpenOCD + Riscv-none-embed. \r\n");

anl_printf("- Test Case : UART Interrupt Demo \r\n");

anl_printf(sys_banner);

anl_printf("---------------------------------------------------------------- \r\n");

这段代码使用 anl_printf 函数打印了一段系统信息。让我们来逐行解释:

  • anl_printf("---------------------------------------------------------------- \r\n");

    打印了一条分隔线,用于美观和区分信息的开头。

  • anl_printf("- SoftCore Demo : RISCV(Freq 80MHz. 32kB RAM. Full Feature) \r\n");

    打印了系统的软核演示信息,包括软核类型(RISCV)、频率(80MHz)、RAM 大小(32kB)和功能(全功能)。

  • anl_printf("- Hardware Platform : EG4S20NG88 PotatoPie V4.0 \r\n");

    打印了硬件平台信息,包括硬件平台型号(EG4S20NG88)和版本(PotatoPie V4.0)。

  • anl_printf("- TD Ver. : TD 5.6.4(97693) \r\n");

    打印了测试开发工具的版本信息,包括版本号(TD 5.6.4)和编译号(97693)。

  • anl_printf("- OS+Build+Debug : Windows, OpenOCD + Riscv-none-embed. \r\n");

    打印了操作系统、构建环境和调试工具的信息,包括操作系统类型(Windows)和使用的调试工具(OpenOCD + Riscv-none-embed)。

  • anl_printf("- Test Case : UART & GPIO Interrupt, SPI Master Demo \r\n");

    打印了测试用例的信息,包括测试的内容(UART & GPIO 中断、SPI 主控演示)。

  • anl_printf(sys_banner);

    打印了之前定义的 sys_banner 字符串,其中包含了编译时间和日期的信息。

  • anl_printf("---------------------------------------------------------------- \r\n");

    再次打印了一条分隔线,用于美观和区分信息的结尾。

 

SetSystickCfg((SYS_FREQ/TIM_TRIG_FREQ),True);

ClrSystickInt();

这两行代码用于配置和清除 SysTick 定时器的设置:

  1. SetSystickCfg((SYS_FREQ/TIM_TRIG_FREQ),True);

    • 这行代码调用了 SetSystickCfg 函数,用于设置 SysTick 定时器的配置。
    • 第一个参数 (SYS_FREQ/TIM_TRIG_FREQ) 表示每秒钟 SysTick 定时器的时钟周期数,它是系统频率 SYS_FREQ 除以定时器触发频率 TIM_TRIG_FREQ 的结果。
    • 第二个参数 True 表示启用 SysTick 定时器。
  2. ClrSystickInt();

    • 这行代码调用了 ClrSystickInt 函数,用于清除 SysTick 中断标志位。
    • 这样做是为了确保在进入主循环之前,任何可能触发的 SysTick 中断都被处理,以免影响程序的正常执行。
 

// 设置中断掩码

SetIntMask(UART1_INTSRC | GPIO_INTSRC);

uart_ClrEvent(UART,0xFF); //清除全部UART中断

ClrIntEvent(0xFFFFFFFF); // 清除所有中断

GPIO_A->OUTPUT_ENABLE=0x0000FFFF;

GPIO_A->GPIO_INTMASK=0xFFFFFFFF; // 允许所有 GPIO 发送中断

这段代码用于配置中断掩码和清除所有中断:

  1. SetIntMask(UART1_INTSRC | GPIO_INTSRC);

    • 这行代码调用了 SetIntMask 函数,用于设置中断掩码。通过将 UART1_INTSRC 和 GPIO_INTSRC 按位或运算,将 UART1 和 GPIO 的中断源加入到中断掩码中。
    • 这样做的目的是指定哪些中断将被允许触发。
  2. uart_ClrEvent(UART, 0xFF);

    • 这行代码调用了 uart_ClrEvent 函数,清除了 UART 的所有中断事件。
    • 第一个参数 UART 指定了要清除中断事件的 UART 模块。
    • 第二个参数 0xFF 表示清除所有类型的 UART 中断事件。
  3. ClrIntEvent(0xFFFFFFFF);

    • 这行代码调用了 ClrIntEvent 函数,清除了所有的中断事件。
    • 传递的参数 0xFFFFFFFF 表示清除所有中断事件。
  4. GPIO_A->OUTPUT_ENABLE = 0x0000FFFF;

    • 这行代码设置了 GPIO_A 模块的输出使能寄存器,使得 GPIO_A 的低 16 位引脚成为输出引脚。
  5. GPIO_A->GPIO_INTMASK = 0xFFFFFFFF;

    • 这行代码设置了 GPIO_A 模块的中断掩码寄存器,允许所有 GPIO_A 引脚产生中断。
 

while (1)

{

GPIO_A->OUTPUT=i;

i=i+1;

Delay(5000000);

anl_printf("Hello Main!\r\n");

}

这个 while 循环是程序的主循环,它会不断地执行以下操作:

  1. GPIO_A->OUTPUT=i;

    • 将变量 i 的值写入 GPIO_A 模块的输出寄存器中,控制 GPIO_A 的输出状态。每次循环迭代,i 的值会递增。
    • 这个操作会改变 GPIO_A 引脚的输出状态,具体的改变依赖于 i 的值。
  2. i=i+1;

    • 递增变量 i 的值,用于改变 GPIO_A 输出状态的控制值。
  3. Delay(5000000);

    • 调用 Delay 函数,使程序停顿一段时间。在这里,函数的参数 5000000 表示延时的周期数,具体延时时长由系统时钟频率决定。
  4. anl_printf("Hello Main!\r\n");

    • 打印字符串 “Hello Main!\r\n” 到串行终端,向用户输出一条消息。

这样,循环将一直重复执行上述步骤,不断改变 GPIO_A 的输出状态,延时一段时间,然后打印一条消息,形成一个周期性的操作。

其它函数

 

void Delay(int cycle)

{

int i;

for (i=0;i<cycle;i++)

asm volatile(

"add x0, x0, x0"

);

}

这个Delay函数是一个简单的延时循环,它通过重复执行一个无操作的操作add x0, x0, x0来等待指定数量的周期。asm volatile语句用于将汇编语言直接嵌入到C代码中,提供一种执行可能无法直接用C表达的机器指令的方法。

它的功能说明:

  • for循环执行cycle次。
  • 在循环内部,它执行一个汇编指令,将寄存器x0的内容加到自身,并将结果存回x0
  • 由于这是一个无操作指令(它将一个值加到自身,实际上什么都没做),它实际上是一个消耗CPU周期的循环,没有执行任何有意义的计算。

这个函数可用于在时间敏感的应用程序中创建延时,例如控制某些操作的时序。然而,需要注意的是,延迟的持续时间取决于各种因素,包括处理器速度和优化设置,因此它可能不太精确,也不适用于不同平台。

 

// 定时器中断服务函数

void TimerISP()

{

#ifdef USE_MTIME

uint64_t mtime_calc;

mtime_calc=GetMTimeCnt()+(SYS_FREQ/TIM_TRIG_FREQ);

SetMTimeCmp(mtime_calc);

#else

ClrSystickInt();

#endif

GPIO_A->OUTPUT = ~(GPIO_A->OUTPUT);

anl_printf("Hello Tick!\r\n");

return;

}

这是定时器中断服务函数 TimerISP。根据编译时是否定义了 USE_MTIME 宏,函数会有不同的行为:

  1. 如果定义了 USE_MTIME

    • 函数首先声明了一个 uint64_t 类型的变量 mtime_calc,用于计算下一次定时器中断的触发时间。
    • 调用 GetMTimeCnt() 函数获取当前的 MTIME 计数值,即当前时间。
    • 根据系统频率 SYS_FREQ 和定时器触发频率 TIM_TRIG_FREQ 计算下一次定时器中断触发的时间点,并将结果存储在 mtime_calc 中。
    • 调用 SetMTimeCmp 函数设置 MTIME 比较寄存器,使得下一次定时器中断在计算得到的时间点触发。
    • 最后,将 GPIO_A 的输出取反,即翻转输出状态,并打印 “Hello Tick!\r\n” 消息。
  2. 如果未定义 USE_MTIME

    • 则调用 ClrSystickInt() 函数清除 SysTick 定时器中断标志。
    • 接着,将 GPIO_A 的输出取反,即翻转输出状态,并打印 “Hello Tick!\r\n” 消息。

无论哪种情况,这个函数都用于定时器中断服务,每当定时器中断触发时,GPIO_A 的输出状态都会取反,并输出一条消息。

 

// UART1 中断服务函数

void UART1_ISP()

{

char recv_value;

while(uart_GetEvent(UART,UartRxVld))

{

uart_write(UART,uart_read(UART));

}

uart_ClrEvent(UART,UartRxBufFull | UartRxBufHfFull);

ClrIntEvent(UART1_INTSRC);

}

这是 UART1 的中断服务函数 UART1_ISP。当 UART1 接收到数据时,该函数会执行以下操作:

  1. 声明一个 char 类型的变量 recv_value,用于存储接收到的数据。

  2. 使用 while 循环结构,调用 uart_GetEvent 函数检查 UART 接收缓冲区是否有数据可用(即接收到有效数据)。如果接收到数据,则进入循环体。

  3. 在循环体内,调用 uart_read 函数读取 UART 接收缓冲区中的数据,并立即将其通过 uart_write 函数写回 UART 发送缓冲区。这实现了一种称为回显(echo)的功能,即将接收到的数据原样发送回去。

  4. 循环继续,直到 UART 接收缓冲区中没有数据可用。

  5. 调用 uart_ClrEvent 函数清除 UART 接收缓冲区满和半满的标志位,以及清除 UART1 中断标志位。

总的来说,该函数实现了 UART1 接收数据并回显的功能,同时清除相关的中断标志位。

 

void GPIO_ISP()

{

anl_printf("Hello Click!\r\n");

ClrIntEvent(GPIO_INTSRC);

}

这是 GPIO 的中断服务函数 GPIO_ISP。当 GPIO 触发中断时,该函数会执行以下操作:

  1. 使用 anl_printf 函数打印字符串 “Hello Click!\r\n”,提示发生了 GPIO 中断。

  2. 调用 ClrIntEvent 函数清除 GPIO 中断源(即 GPIO_INTSRC 对应的中断标志位),以确认已处理完中断事件。

 

void ExternalISP()

{

uint32_t temp;

temp=GetIntEvent();

if(temp&UART1_INTSRC)

UART1_ISP();

if(temp&GPIO_INTSRC)

GPIO_ISP();

else

{

anl_printf("UNKNOWN INT SRC! SRC= 0x%x",temp);

while(1);

}

}

这是外部中断服务函数 ExternalISP。当发生外部中断时,该函数会执行以下操作:

  1. 声明一个 uint32_t 类型的变量 temp,用于存储当前发生的中断事件。

  2. 调用 GetIntEvent 函数获取当前的中断事件。

  3. 使用条件语句检查中断事件的类型:

    • 如果 temp 中包含 UART1_INTSRC 标志位,则执行 UART1 的中断服务函数 UART1_ISP
    • 如果 temp 中包含 GPIO_INTSRC 标志位,则执行 GPIO 的中断服务函数 GPIO_ISP
    • 如果 temp 中不包含以上任何一种中断源的标志位,则打印消息 “UNKNOWN INT SRC! SRC= 0x%x”,其中 %x 是 temp 的十六进制表示,表示未知的中断来源,并进入无限循环。

工程源码在这个路径:

实验结果:

在 《PotatoPie 4.0 实验教程(40) —— FPGA实现RISC-V工程创建和调试》教程里我们知道如何创建,编译下载和调试RISCV工程,我们这里先只编译FD工程,而不用FD进行下载。

在TD中我们需要修改TD Soc_Top.v文件中的TCM0_INITFILE为,并重新编译TD工程。

parameter TCM0_INITFILE="../../../../../wkspace/hello_world/Debug/hello_world.bin.mif";

然后打开看串口助手之类的工具,设置串口波特率为115200,8N1模式。

最后后用TD工具的JTAG下载功能下载到FPGA之中,在串口工具中我们将看到如下输出:

同时可以看到板上蓝灯一秒一闪烁。

如果你的FPGA中已下载过之前的RISCV软核位流,那么可以也可以直接在FD中点击这个进行代码运行,而不用重新编译和下载FPGA程序。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/579495.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

数据集市的详细建设方案!

▶ 什么是数据集市&#xff1f; 数据集市是处理单一事务的数据仓库的子集。它们通常由单个业务部门构建和管理。由于它们是面向主题的&#xff0c;因此通常仅从少数来源获取数据&#xff0c;这些来源可能是内部操作系统&#xff0c; 数据湖&#xff0c;一个集中的 数据存储库&a…

c++理论篇(一) ——浅谈tcp缓存与tcp的分包与粘包

介绍 在网络通讯中,Linux系统为每一个socket创建了接收缓冲区与发送缓冲区,对于TCP协议来说,这两个缓冲区是必须的.应用程序在调用send/recv函数时,Linux内核会把数据从应用进程拷贝到socket的发送缓冲区中,应用程序在调用recv/read函数时,内核把接收缓冲区中的数据拷贝到应用…

Android 设置头像 - 相册拍照

Android开发在个人信息管理中&#xff0c;如果设置头像&#xff0c;一般都提供了从相册选择和拍照两种方式。下午将针对设置用户头像相册和拍照两种方式的具体实现进行详细说明。 在实际实现过程中需要使用到权限管理&#xff0c;新版本的Android需要动态申请权限&#xff0c;权…

rabbitmq下载安装最新版本--并添加开机启动图文详解!!

一、简介 RabbitMQ是一个开源的遵循AMQP协议实现的消息中间件支持多种客户端语言,用于分布式系统中存储和转发消息, 这是 Release RabbitMQ 3.13.0 rabbitmq/rabbitmq-server GitHub 二、安装前准备 1、查看自己系统 确认操作系统版本兼容性 uname -a2、下载Erlang依赖包…

【12580无线通信技术】第十一章 Ad hoc网络无线通信技术期末复习自考复习

第十一章 Ad hoc网络无线通信技术 P283&#xff08;名词&#xff09;Ad hoc技术&#xff1a;是一种特定的无线网络结构&#xff0c;强调的是多跳、自组织、无中心的概念。P285&#xff08;简答&#xff09;Ad hoc网络的特点:①自组织和无中心特性&#xff1b;②网络拓补动态变…

SpringCloud系列(20)--Ribbon的简介及使用

1、Ribbon的简介 Spring Cloud Ribbon是基于Netflix Ribboh实现的一套客户端负载均衡的工具&#xff0c;简单的说&#xff0c;Ribbon是Netflix发布的开源项目&#xff0c;主要功能是提供客户端的软件负载均衡算法和服务调用。Ribbon客户端组件提供一系列完善的配置项如连接超时…

学习100个Unity Shader (14) ---透明效果

文章目录 渲染队列透明度测试&#xff08;Alpha Test&#xff09;效果Shader 透明度混合&#xff08;Alpha Blending&#xff09;效果Shader 参考 渲染队列 由”Queue“ 标签决定&#xff0c;索引号越小越早被渲染&#xff1a; 名称队列索引号Background1000Geometry2000Alph…

论文阅读之MMSD2.0: Towards a Reliable Multi-modal Sarcasm Detection System

文章目录 论文地址主要内容主要贡献模型图技术细节数据集改进多视图CLIP框架文本视图图像视图图像-文本交互视图 实验结果 论文地址 https://arxiv.org/pdf/2307.07135 主要内容 这篇文章介绍了一个名为MMSD2.0的多模态讽刺检测系统的构建&#xff0c;旨在提高现有讽刺检测系…

通过大模型(LLM)的多模态辩论的恶意表情包识别

Towards Explainable Harmful Meme Detection through Multimodal Debate between Large Language Models https://arxiv.org/abs/2401.13298https://arxiv.org/abs/2401.13298 1.概论 对于恶意表情包的识别,以往的研究方法没有能够深入表情包所隐含的复杂意义和文化背景,因…

vue-manage-system 更新,后台管理系统开发更简单

vue-manage-system 近期进行了一次版本升级&#xff0c;主要是支持了更多功能、升级依赖版本和优化样式&#xff0c;并且上线了官方文档网站&#xff0c;大部分功能都有文档或者使用示例&#xff0c;更加适合新手上手开发&#xff0c;只需要根据实际业务简单修改&#xff0c;就…

用fgets()替换fscanf()解决文件读取在小熊猫C++失败问题

fscanf&#xff08;&#xff09;遇到空格就结束读取&#xff0c;导致文件读取数据没完就退出读取以至于不能导入游戏地图工程。 看看到右侧小方块轨迹知晓采样区移动情况 也已经实现摄像机追随玩家效果 // 程序&#xff1a;2D RPG 地图编辑器与摄像机追随 // 作者&#xff1…

C语言自定义类型【联合体与枚举】

文章目录 1.联合体1.1联合体的声明1.2联合体的特点1.3联合体的大小计算联合体的使用案例 2.枚举2.1枚举类型的声明2.2枚举类型的优点(为什么使用枚举)2.3枚举类型的使用 结语 1.联合体 1.1联合体的声明 和结构体一样&#xff0c;联合体也是由一个或多个成员构成&#xff0c;同…

如何在 Visual Studio 中通过 NuGet 添加包

在安装之前要先确定Nuget的包源是否有问题。 Visual Studio中怎样更改Nuget程序包源-CSDN博客 1.图形界面安装 打开您的项目&#xff0c;并在解决方案资源管理器中选择您的项目。单击“项目”菜单&#xff0c;然后选择“管理 NuGet 程序包”选项。在“NuGet 包管理器”窗口中…

CTF(web方向)--md5的“===”和“==”的绕过

一、PHP弱类型说明 1.简介 php是一种弱类型语言&#xff0c;对数据的类型要求并不严格&#xff0c;可以让数据类型互相转换。 在php中有两种比较符号: 一种是 &#xff0c;另外一种是 &#xff0c;都是用来比较两个数值是否相等的操作符&#xff0c;但他们也是有区别的: &a…

大数据架构相关知识总结

一、大数据处理系统架构特性 1. 鲁棒性和容错性&#xff1a; 系统必须对游bug的程序写入的错误数据游足够的适应能力 2. 低延迟读取和更新能力 3. 横向扩容&#xff1a; 可以通过增加机器数量来维持性能 4. 通用性&#xff1a; 需要支持绝大多数应用程序 5. 延展性&#xff1a;…

前端工程化Vue使用Node.js设置国内高速npm镜像源(踩坑记录版)

前端工程化Vue使用Node.js设置国内高速npm镜像源&#xff08;踩坑记录版&#xff09; 此篇仅为踩坑记录&#xff0c;并未成功更换高速镜像源&#xff0c;实际解决方法见文末跳转链接。 1.自身源镜像 自身镜像源创建Vue项目下载速度感人 2.更改镜像源 2.1 通过命令行配置 前提…

【工作】程序员工作压力八个常见来源与建议缓解压力小窍门

目录 ​编辑 一. 程序员工作压力八个常见来源与建议 1&#xff09;目标职位不对 2&#xff09;工作任务描述不清晰 3&#xff09;快节奏的工作环境 4&#xff09;项目后期突然被添加新的要求 5&#xff09;计划外的工作事务会打断并破坏注意力 6&#xff09;个人问题 7…

MySQL第一次作业

解压完安装包 以管理员进入命令行 初始化并记住初始随机密码 创建服务名称 启动mysql 使用随机密码登录 修改密码 退出并重登服务器 MySQL创建数据库和表 创建数据库 创建表 1.进入数据库 创建表 向表中插入数据

鸿蒙OpenHarmony【小型系统 编译】(基于Hi3516开发板)

编译 OpenHarmony支持hb和build.sh两种编译方式。此处介绍hb方式&#xff0c;build.sh脚本编译方式请参考[使用build.sh脚本编译源码]。 使用build.sh脚本编译源码 进入源码根目录&#xff0c;执行如下命令进行版本编译。 ./build.sh --product-name name --ccache 说明&…

[Java EE] 多线程(四):线程安全问题(下)

1.5 volatile关键字 我们在了解这个关键字之前,我们首先要把产生线程安全的第4个原因补齐,我们来说说由于内存可见性引起的线程安全问题. 我们来看下面这样一段代码: import java.util.Scanner;public class Demo16 {public static int count 0;public static void main(Str…