【旧文更新】【优秀课设】基于FPGA的Verilog HDL自动售货机

【旧文更新】基于FPGA的Verilog HDL自动售货机

文章目录

  • 关于旧文新发
  • FPGA
    • Cortex-M架构SysTick系统定时器阻塞和非阻塞延时
  • 附录:压缩字符串、大小端格式转换
    • 压缩字符串
      • 浮点数
      • 压缩Packed-ASCII字符串
    • 大小端转换
      • 什么是大端和小端
      • 数据传输中的大小端
      • 总结
      • 大小端转换函数
  • 附录:关于旧文新发

关于旧文新发

为何要进行旧文新发?
因为我在2023年博客之星评选中发现 有的人转载、抄袭他人文章 稍微改动几下也能作为高质量文章入选
所以我将把我的旧文重新发一次 然后也这样做

2023年博客之星规则:
在这里插入图片描述

FPGA

左边9个拨动开关是10元商品的选择器,右边9个拨动开关是15元商品的选择器,需要什么商品就上拨开关,然后商品的总钱数会显示在左边两个数码管上(例如我上拨左边2个,右边1个,数码管就应该显示为210+115=35)

左边两个按键分别为进钱5元,进钱10元,按按几下,就是进了几次对应的钱,然后将总钱数显示在中间的数码管上

右边两个按键分别为确定出货和清零,当按下确定建时,如果进钞总数大于商品所需钱数,就将剩余的钱数显示在左边的数码管上,中间数码管置零,如果钱不够,就进行LED灯的闪烁警告。

当按清零按键时,就将所有数码管置零。

代码如下:

module TCD1206(C5,C10,F,S10,S15,Y5);
//程序开始的顶层文件名和总变量input C5, C10, F;		//定义输入output S10, S15, Y5;		//定义输出reg S10, S15, Y5;		//定义寄存器类型的三种输出变量
wire s10_enable,s15_enable,s20_enable;		//定义线型使能信号变量
reg [3:0] s_count;		//定义6位RS计数器变量parameter s_end = 'h5,		//赋值RS使能信号的结束值
s20_begin = 'h4,
s15_begin = 'h3, 	//SH结束值
s10_begin = 'h2; 	//SH开始值		占空比1/10 周期1msassign s10_enable = ((s_count>=s10_begin));
//赋值RS使能信号范围
assign s15_enable = ((s_count>=s15_begin));
//赋值SH使能信号范围
assign s20_enable = ((s_count>=s20_begin));
//赋值 CR1、CR2使能信号范围//RS信号产生部分
always@(posedge C5 or posedge C10)
//always语句块 CLK和RST上升沿有效
begin 		//语句块开始
if (C10)	s_count<=s_count+1'b1+1'b1;		//当RST信号变为高电平时 6位计数器置0
else if (s_count>=s_end -1 )  s_count <= 3'h0;
//当计数器超过最大值-1时 计数器清零
else	s_count<=s_count+1'b1;
//其他范围内计数器加一位二进制的1
end			//结束语句块always@(posedge F)
begin
if (!F) S10<=1'b0,S15<=1'b0;
else if (s20_enable) Y5<= 1'b1,S15<=1'b0,s_count<=1'b0;
else if (s15_enable) S15<= 1'b1 ,s_count<=1'b0;
else if (s10_enable) S10<= 1'b1 ,s_count<=1'b0;
//当RS使能信号高电平时RS信号置1
else	S10<=1'b0,S15<=1'b0,Y5<= 1'b0; 		//其他时候RS为0
end	endmodule		//程序结束

Cortex-M架构SysTick系统定时器阻塞和非阻塞延时

首先是最常用的阻塞延时

void delay_ms(unsigned int ms)
{SysTick->LOAD = 50000000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器while(ms--){while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待}SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}
void delay_us(unsigned int us)
{SysTick->LOAD = 50000000/1000/1000-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器while(us--){while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待}SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}

50000000表示工作频率
分频后即可得到不同的延时时间
以此类推

那么 不用两个嵌套while循环 也可以写成:

void delay_ms(unsigned int ms)
{SysTick->LOAD = 50000000/1000*ms-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}
void delay_us(unsigned int us)
{SysTick->LOAD = 50000000/1000/1000*us-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}

但是这种写法有个弊端
那就是输入ms后,最大定时不得超过计数值,也就是不能超过LOAD的最大值,否则溢出以后,则无法正常工作

而LOAD如果最大是32位 也就是4294967295

晶振为50M的话 50M的计数值为1s 4294967295计数值约为85s

固最大定时时间为85s

但用嵌套while的话 最大可以支持定时4294967295*85s

如果采用非阻塞的话 直接改写第二种方法就好了:

void delay_ms(unsigned int ms)
{SysTick->LOAD = 50000000/1000*ms-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器//while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待//SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}
void delay_us(unsigned int us)
{SysTick->LOAD = 50000000/1000/1000*us-1; // Count from 255 to 0 (256 cycles)  载入计数值 定时器从这个值开始计数SysTick->VAL = 0; // Clear current value as well as count flag  清空计数值到达0后的标记SysTick->CTRL = 5; // Enable SysTick timer with processor clock  使能26MHz的系统定时器//while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set  等待//SysTick->CTRL = 0; // Disable SysTick  关闭系统定时器
}

将等待和关闭定时器语句去掉
在使用时加上判断即可变为阻塞:

delay_ms(500);
while ((SysTick->CTRL & 0x00010000)==0);
SysTick->CTRL = 0;

在非阻塞状态下 可以提交定时器后 去做别的事情 然后再来等待

不过这样又有一个弊端 那就是定时器会自动重载 可能做别的事情以后 定时器跑过了 然后就要等85s才能停下

故可以通过内部定时器来进行非阻塞延时函数的编写

基本上每个mcu的内部定时器都可以配置自动重载等功能 网上资料很多 这里就不再阐述了

附录:压缩字符串、大小端格式转换

压缩字符串

首先HART数据格式如下:
在这里插入图片描述
在这里插入图片描述
重点就是浮点数和字符串类型
Latin-1就不说了 基本用不到

浮点数

浮点数里面 如 0x40 80 00 00表示4.0f

在HART协议里面 浮点数是按大端格式发送的 就是高位先发送 低位后发送

发送出来的数组为:40,80,00,00

但在C语言对浮点数的存储中 是按小端格式来存储的 也就是40在高位 00在低位
浮点数:4.0f
地址0x1000对应00
地址0x1001对应00
地址0x1002对应80
地址0x1003对应40

若直接使用memcpy函数 则需要进行大小端转换 否则会存储为:
地址0x1000对应40
地址0x1001对应80
地址0x1002对应00
地址0x1003对应00

大小端转换:

void swap32(void * p)
{uint32_t *ptr=p;uint32_t x = *ptr;x = (x << 16) | (x >> 16);x = ((x & 0x00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF);*ptr=x;
}

压缩Packed-ASCII字符串

本质上是将原本的ASCII的最高2位去掉 然后拼接起来 比如空格(0x20)
四个空格拼接后就成了
1000 0010 0000 1000 0010 0000
十六进制:82 08 20
对了一下表 0x20之前的识别不了
也就是只能识别0x20-0x5F的ASCII表
在这里插入图片描述

压缩/解压函数后面再写:

//传入的字符串和数字必须提前声明 且字符串大小至少为str_len 数组大小至少为str_len%4*3 str_len必须为4的倍数
uint8_t Trans_ASCII_to_Pack(uint8_t * str,uint8_t * buf,const uint8_t str_len)
{if(str_len%4){return 0;}uint8_t i=0;memset(buf,0,str_len/4*3);	  for(i=0;i<str_len;i++){if(str[i]==0x00){str[i]=0x20;}}for(i=0;i<str_len/4;i++){buf[3*i]=(str[4*i]<<2)|((str[4*i+1]>>4)&0x03);buf[3*i+1]=(str[4*i+1]<<4)|((str[4*i+2]>>2)&0x0F);buf[3*i+2]=(str[4*i+2]<<6)|(str[4*i+3]&0x3F);}return 1;
}//传入的字符串和数字必须提前声明 且字符串大小至少为str_len 数组大小至少为str_len%4*3 str_len必须为4的倍数
uint8_t Trans_Pack_to_ASCII(uint8_t * str,uint8_t * buf,const uint8_t str_len)
{if(str_len%4){return 0;}uint8_t i=0;memset(str,0,str_len);for(i=0;i<str_len/4;i++){str[4*i]=(buf[3*i]>>2)&0x3F;str[4*i+1]=((buf[3*i]<<4)&0x30)|(buf[3*i+1]>>4);str[4*i+2]=((buf[3*i+1]<<2)&0x3C)|(buf[3*i+2]>>6);str[4*i+3]=buf[3*i+2]&0x3F;}return 1;
}

大小端转换

在串口等数据解析中 难免遇到大小端格式问题

什么是大端和小端

所谓的大端模式,就是高位字节排放在内存的低地址端,低位字节排放在内存的高地址端。

所谓的小端模式,就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。

简单来说:大端——高尾端,小端——低尾端

举个例子,比如数字 0x12 34 56 78在内存中的表示形式为:

1)大端模式:

低地址 -----------------> 高地址

0x12 | 0x34 | 0x56 | 0x78

2)小端模式:

低地址 ------------------> 高地址

0x78 | 0x56 | 0x34 | 0x12

可见,大端模式和字符串的存储模式类似。

数据传输中的大小端

比如地址位、起止位一般都是大端格式
如:
起始位:0x520A
则发送的buf应为{0x52,0x0A}

而数据位一般是小端格式(单字节无大小端之分)
如:
一个16位的数据发送出来为{0x52,0x0A}
则对应的uint16_t类型数为: 0x0A52

而对于浮点数4.0f 转为32位应是:
40 80 00 00

以大端存储来说 发送出来的buf就是依次发送 40 80 00 00

以小端存储来说 则发送 00 00 80 40

由于memcpy等函数 是按字节地址进行复制 其复制的格式为小端格式 所以当数据为小端存储时 不用进行大小端转换
如:

uint32_t dat=0;
uint8_t buf[]={0x00,0x00,0x80,0x40};memcpy(&dat,buf,4);float f=0.0f;f=*((float*)&dat); //地址强转printf("%f",f);

或更优解:

   uint8_t buf[]={0x00,0x00,0x80,0x40};   float f=0.0f;memcpy(&f,buf,4);

而对于大端存储的数据(如HART协议数据 全为大端格式) 其复制的格式仍然为小端格式 所以当数据为小端存储时 要进行大小端转换
如:

uint32_t dat=0;
uint8_t buf[]={0x40,0x80,0x00,0x00};memcpy(&dat,buf,4);float f=0.0f;swap32(&dat); //大小端转换f=*((float*)&dat); //地址强转printf("%f",f);

或:

uint8_t buf[]={0x40,0x80,0x00,0x00};memcpy(&dat,buf,4);float f=0.0f;swap32(&f); //大小端转换printf("%f",f);

或更优解:

uint32_t dat=0;
uint8_t buf[]={0x40,0x80,0x00,0x00};float f=0.0f;dat=(buf[0]<<24)|(buf[0]<<16)|(buf[0]<<8)|(buf[0]<<0)f=*((float*)&dat);

总结

固 若数据为小端格式 则可以直接用memcpy函数进行转换 否则通过移位的方式再进行地址强转

对于多位数据 比如同时传两个浮点数 则可以定义结构体之后进行memcpy复制(数据为小端格式)

对于小端数据 直接用memcpy写入即可 若是浮点数 也不用再进行强转

对于大端数据 如果不嫌麻烦 或想使代码更加简洁(但执行效率会降低) 也可以先用memcpy写入结构体之后再调用大小端转换函数 但这里需要注意的是 结构体必须全为无符号整型 浮点型只能在大小端转换写入之后再次强转 若结构体内采用浮点型 则需要强转两次

所以对于大端数据 推荐通过移位的方式来进行赋值 然后再进行个别数的强转 再往通用结构体进行写入

多个不同变量大小的结构体 要主要字节对齐的问题
可以用#pragma pack(1) 使其对齐为1
但会影响效率

大小端转换函数

直接通过对地址的操作来实现 传入的变量为32位的变量
中间变量ptr是传入变量的地址

void swap16(void * p)
{uint16_t *ptr=p;uint16_t x = *ptr;x = (x << 8) | (x >> 8);*ptr=x;
}void swap32(void * p)
{uint32_t *ptr=p;uint32_t x = *ptr;x = (x << 16) | (x >> 16);x = ((x & 0x00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF);*ptr=x;
}void swap64(void * p)
{uint64_t *ptr=p;uint64_t x = *ptr;x = (x << 32) | (x >> 32);x = ((x & 0x0000FFFF0000FFFF) << 16) | ((x >> 16) & 0x0000FFFF0000FFFF);x = ((x & 0x00FF00FF00FF00FF) << 8) | ((x >> 8) & 0x00FF00FF00FF00FF);*ptr=x;
}

附录:关于旧文新发

为何要进行旧文新发?
因为我在2023年博客之星评选中发现 有的人转载、抄袭他人文章 稍微改动几下也能作为高质量文章入选
所以我将把我的旧文重新发一次 然后也这样做

2023年博客之星规则:

  1. 自2023年1月1日起算起,平均每周创作过至少一篇高质量且非付费专栏的原创文章即可入围。由于博客之星是年度评选,所以统计时间一直截止到2023年12月17日。
  2. 高质量博文为80分以上原创博文,质量分查询地址:https://www.csdn.net/qc
  3. 入围条件补充说明:当前的入围状态为动态,一旦未达到每周平均创作过至少一篇高质量且非付费专栏的原创文章入围资格将会跳出入围资格,若当前还未入围者通过后期创作也可入围,当下并非最终结果。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Three.js 中的场景与相机基础

Three.js 中的场景与相机基础 一、场景&#xff08;Scene&#xff09; 在 Three.js 中&#xff0c;场景是所有 3D 对象存在和交互的容器。艾斯视觉作为行业ui设计与前端开发服务商很高兴能在这里与你共同探讨&#xff1a;它就像是一个虚拟的 3D 空间&#xff0c;我们可以在其中…

端午节趣味互动小游戏的作用是什么

端午节吃粽子&#xff0c;多数行业商家都可借势进行品牌营销&#xff0c;而一场营销效果的优劣&#xff0c;除了好方案外&#xff0c;还需要好的工具/渠道及运营等&#xff0c;围绕粽子元素的互动小游戏是营销互动的主要形式之一。 运用【雨科】平台拥有多款端午节粽子主题互动…

leetcode 1270 向公司CEO汇报工作的所有人(postgresql)

需求 员工表&#xff1a;Employees ---------------------- | Column Name | Type | ---------------------- | employee_id | int | | employee_name | varchar | | manager_id | int | ---------------------- employee_id 是这个表的主键。 这个表中每一行中&#xff0c;e…

用于脑肿瘤分割的跨模态深度特征学习| 文献速递-深度学习肿瘤自动分割

Title 题目 Cross-modality deep feature learning for brain tumor segmentation 用于脑肿瘤分割的跨模态深度特征学习 01 文献速递介绍 作为最致命的流行病&#xff0c;脑肿瘤的研究越来越受到关注。本文研究了一种基于深度学习的自动分割胶质瘤的方法&#xff0c;称为脑…

raid 磁盘阵列

raid 磁盘阵列&#xff1a; **条带化存储&#xff1a;**数据分散在多个物理磁盘上的存储方式。利用多个磁盘并行读取和写入。 条带化的存储性能和读写性能是最好的。 磁盘的组成方式不同&#xff0c;也就是不同的raid级别&#xff1a; **raid 0&#xff08;条带化存储&#x…

高级软件工程师带你解决MySQL死锁问题Deadlock found when trying to get lock实战案例全网唯一

高级软件工程师带你解决MySQL死锁问题Deadlock found when trying to get lock实战 前言 笔者我实际工作中&#xff0c;经常遇到mysql数据库死锁问题&#xff01;CSDN上面也有很多大神刨析解决方法&#xff01; 我在实际工作中的MySQL数据库架构很复杂&#xff0c;又涉及到主…

MT3045 松鼠接松果

思路&#xff1a; 求x的一个区间&#xff0c;使区间中的松果的最大y坐标和最小y坐标的差至少为D。若有多个区间&#xff0c;则取最小的那个。 即使用单调队列不断维护最大值和最小值。 首先L固定不动&#xff0c;R不断右移&#xff1a; 即若函数f(R)max[L,R]-min[L,R] >…

自建视频托管平台:MediaCMS

目录 1 MediaCMS简介1.1 介绍1.2 特性1.3 应用场景 2 安装配置2.1 安装1、安装2、汉化 2.2 一些常见配置 3 简单使用3.1 上传3.2 下载3.3 添加标题或者字幕3.4 通过Tag/Category实现视频/文件分类添加 Tag给任一资源分类 1 MediaCMS简介 1.1 介绍 MediaCMS是一个现代的&#…

Spring +SpringMVC+Mybatis项目详细构造

一&#xff0c;文档详解 1&#xff0c;web.xml配置 配置spring监听器&#xff1a; 指定spring配置文件的位置和名称&#xff0c;扫描会先扫描此文件&#xff0c;此文件中的扫描文档作为父类扫描&#xff0c;父类扫描不可访问子类扫描&#xff0c;子类扫描可访问父类扫描 &l…

LeetCode --- 399周赛

题目列表 3162. 优质数对的总数 I 3163. 压缩字符串 III 3164. 优质数对的总数 II 3165. 不包含相邻元素的子序列的最大和 一、优质数对的总数I 这里由于数据范围比较小&#xff0c;我们可以直接暴力枚举&#xff0c;代码如下 class Solution { public:int numberOfPairs…

5292A 物联网信号分析仪

5292A 物联网信号分析仪 —— 10MHz&#xff5e;6GHz —— 简述 5292A物联网信号分析仪是一款通用的矢量信号分析仪&#xff0c;频率范围覆盖 10MHz&#xff5e;6GHz&#xff0c;具有良好的频率、功率测量精度和稳定度&#xff1b;支持模拟与数字调制信号、全制式的通信标准…

【错误记录】HarmonyOS 运行报错 ( Failure[MSG_ERR_INSTALL_FAILED_VERIFY_APP_PKCS7_FAIL] )

文章目录 一、报错信息二、问题分析二、解决方案 一、报错信息 在 DevEco Studio 中 , 运行程序 , 编译时正常编译 , 但是在真机运行时 , 报如下错误 , 核心报错信息是 " Failure[MSG_ERR_INSTALL_FAILED_VERIFY_APP_PKCS7_FAIL] " ; 完整报错信息 : 05/29 10:58:55…

【机器学习300问】104、残差网络是怎么起作用的?

残差网络&#xff08;Residual Network&#xff0c;简称ResNet&#xff09;诞生是为了解决深度神经网络的训练困难性问题。深度神经网络在图像分类等任务上取得了重大突破&#xff0c;但随着网络层数的增加&#xff0c;训练变得更加困难。 一、神经网络深度过深会出现…

用docker搭建的Vulfocus镜像管理界面没有镜像可以拉取解决办法

ps&#xff1a;截止到今天2023.4.2&#xff0c;kali和vps的docker拉取的vulfocus镜像会有版本的区别&#xff0c;虽然都是拉取的最新版&#xff0c;vps上镜像为3个月以前&#xff0c;kali上为16个月以前&#xff0c;所以在修改 views.py 文件时&#xff0c;可能会发现文件内容不…

国家首批20个“数据要素×”典型案例发布,珈和科技助力金融、气象两案例入榜

5月24日&#xff0c;国家数据局会同生态环境部、交通运输部、金融监管总局、中国科学院、中国气象局、国家文物局、国家中医药局等部门在第七届数字中国峰会主论坛上发布首批20个“数据要素”典型案例。 其中&#xff0c;珈和科技以其专业的产品和服务助力金融服务领域的浙江网…

GDPU 操作系统 天码行空13

文章目录 ❌ TODO&#xff1a;本文仅供参考&#xff0c;极有可能有误1.生产者消费者问题&#xff08;信号量&#xff09;&#x1f496; ProducerConsumerExample.java&#x1f3c6; 运行结果 &#x1f496; ProducerConsumerSelectiveExample.java&#x1f3c6; 运行结果 2.实现…

【UnityShader入门精要学习笔记】第十五章 使用噪声

本系列为作者学习UnityShader入门精要而作的笔记&#xff0c;内容将包括&#xff1a; 书本中句子照抄 个人批注项目源码一堆新手会犯的错误潜在的太监断更&#xff0c;有始无终 我的GitHub仓库 总之适用于同样开始学习Shader的同学们进行有取舍的参考。 文章目录 使用噪声上…

【JS基础语法04】运算符分类以及运用

一&#xff1a;赋值运算符 1 类型 赋值运算符包括以下&#xff1a;、、-、*、/ 2 原理 &#xff0c;是将等号右边的数赋值给左边以为例(-、*、/和运算逻辑是相同的) let num 5 num2 等价于 let num 5 numnum2 //num7 二&#xff1a;一元运算符 1怎么判断运算符是几元…

多条文本转二维码怎么做?一键批量建码的使用技巧

怎么快速的制作多条文本信息的二维码&#xff1f;随着二维码的广泛使用&#xff0c;现在很多内容都可以通过生成二维码扫码的方式来获取信息&#xff0c;其他文本二维码就是比较常用的一种类型。那么当需要将多条不同的文本内容每条单独生成二维码时&#xff0c;有什么方法可以…

【Uniapp微信小程序】自定义水印相机、微信小程序地点打卡相机

效果图 template 下方的image图片自行寻找替换! <template><view><camerav-if="!tempImagePath && cameraHeight !== 0":resolution="high":frame-size="large":device-position="device":flash="flas…