Verilog HDL实现智能药盒

本文档详细介绍了使用VerilogHDL设计智能药盒的过程,该药盒具备定时提醒老年人吃药的功能。代码包括主模块、按键消抖模块和时钟分频模块。主模块中,通过按键设置药盒的提醒时间,启动装置后,装置会根据设定时间自动提醒吃药,提醒方式为LED闪烁和蜂鸣器。在正式工作状态下,通过不同频率的时钟控制LED闪烁和蜂鸣器提醒,以指示哪个药盒需要取药。此外,还实现了按键消抖,避免了因按键抖动造成的误操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Verilog HDL实现智能药盒

一、简介

在这里插入图片描述

我们知道,当前,老年人的数量是比较多的,从而导致了照顾老年人成为了一个比较大的社会问题。由于老年人的记忆力相比之前可能是会有所下降的,而且与此同时,由于老年人的子女们也有上班工作等一些其他的生活的需求,所以说,老年人的身边并不是总能有人在陪伴的,这就导致了一些社会性的问题。例如,很多老年人随着年龄的增长,会伴随着健忘的问题。对于老年人来说,在生病的时候,甚至是日常生活中按时吃药都是一个难题。如果需要在不同时间吃不同的药,几乎很难独立完成。而设计实现一个具有计时提醒功能的智能药盒就是为这些老年人所设计的。

我们前面已经说了,其中的问题之一,就是老年人如果生病需要吃药的话,很有可能会由于自己的疏忽,忘记了吃药这件事,而同时身边也没有人来即是的提醒,从而会使得老年人错过正确的吃药的时间,这就有可能对老年人的病情产生一些不利的影响,更严重的话,甚至有可能会带来生命危险的。因此,我们需要制作一种可以自动的提醒老年人进行在设置的时间去吃药的操作的一种智能药盒。这种药盒,我们在设计的时候,可以一次性设定最多三种药的吃药时间,对老年人进行提醒吃药的操作,如果老年人吃过了药,那么提醒的警报就会消失,否则,会一直进行提醒。这样一来就可以在正确的时间来提醒老年人吃药,从而减小了错过吃药的时间的可能性了,更好的保证了老年人的安全与健康。

本实验旨在设计一种模拟的智能药盒,主要功能是进行模拟智能药盒的设定提醒时间以及定时的提醒吃药的功能。也就是说,本实验模拟的药盒可以实现定时的操作,设计的时候由于器件等原因,一次最多可以设置三种药的吃药的时间,然后在启动装置以后,装置就会自动的计时,等达到了某一种药的吃药的时间的时候,系统就会自动提醒吃药啦,吃过药以后,提醒关闭。就这样的一直循环下去,直到人为的关闭系统为止。当系统再一次启动的时候,仍然可以重新设置定时以及后面的提醒工作可以正常的进行。

在这里插入图片描述

由于后面内容比较多,这里先来一个现象:

https://www.bilibili.com/video/BV1MY411x7Xw?spm_id_from=333.999.0.0

Verilog HDL实现智能药盒

下面开始正式的项目的制作了啦:

二、代码

1、主文件(主模块的代码)

代码比价多、比较长,但是里面有详细的注释。

module test_little_foot(clk,rst,btn1,btn2,btn3,btn4,btn5,btn6,
								rled1, rled2, rled3,rled4,seg_led_1,seg_led_2,
								row, col);// row, col 是点阵的设置。
								
								// 进行小脚丫上面的所有的代码的一个整合啦。
								
	output	reg	[7:0]	row;
	output	reg	[7:0]	col;
	// Dot Matrix.---点阵。
	
	localparam	s0 = 3'd0, s1 = 3'd1, s2 = 3'd2, s3 = 3'd3,
			s4 = 3'd4, s5 = 3'd5, s6 = 3'd6, s7 = 3'd7;  
			// 这个状态是进行扫描的时候使用的啦。
			
	// Dot Matrix 显示的时候所需要的控制状态。
	
		
	reg	[12:0]	cnt;
	always @(posedge clk or negedge rst)
		if(!rst) cnt <= 1'b0;
		else if(cnt >= 13'd7499) cnt <= 1'b0;
		else cnt <= cnt + 1'b1;

	reg	clk_800hz;
	// 扫描的频率是 800 Hz。
	always @(posedge clk or negedge rst)
		if(!rst) clk_800hz <= 1'b0;
		else if(cnt == 13'd7499) clk_800hz <= ~clk_800hz;
		// 时钟分频。
		else clk_800hz <= clk_800hz;
		
// 产生扫描的频率哦。

//reg	[12:0]	cnt;
//always @(posedge clk or negedge rst_n)
//	if(!rst_n) cnt <= 1'b0;
//	else if(cnt >= 13'd7499) cnt <= 1'b0;
//	else cnt <= cnt + 1'b1;
//
//reg	clk_800hz;
//always @(posedge clk or negedge rst_n)
//	if(!rst_n) clk_800hz <= 1'b0;
//	else if(cnt == 13'd7499) clk_800hz <= ~clk_800hz;
//	else clk_800hz <= clk_800hz;
//	
	
	input clk;
	// 时钟信号。
   input rst;
	// 重置信号,在2.0版本无使用。
	
   input btn1;  
	// 设置盒子开机。
	input btn2;
	// 设置三个药箱中具体操作哪一个。
	
	input btn3;  // 按下一次,时间加一秒;
	input btn4;  // 按下一次,时间减一秒。
	// 设置时间。
	
	input btn5;
	// 设置完毕时间以后,需要有一个按键来开启装置的工作状态。
	
	input btn6;
	// 关闭挺提醒的警报的按键。
	
	output rled1;       
	output rled2;  
	output rled3;
	output rled4;
	// 输出四个led灯,前三个是对应于三个药盒,第四个原本要接蜂鸣器,但是小脚丫没有。
	
	reg led1;       
	reg led2;  
	reg led3; 
	reg led4 = 1;
	//	led4 = 1,表示在没有正式进入工作转态的时候,是处于熄灭的状态。
	// 记录在没有正式进入工作状态的时候的信号。
	
	reg led10;       
	reg led20;  
	reg led30; 
	reg led40;
	// 记录在工作状态下的时候的信号。
	
	reg times;
	// 设置一个数字来判断是否需要进行 counting 置零的操作啦。
	
	reg times0;
	// 设置一个数字来判断是否进行了置零的操作了。
	
	wire times_ = times;
	// 连线相接。
	
	wire times0_ = times0;
	// 连线相接。
	
	output [8:0] seg_led_1;
	output [8:0] seg_led_2;
	// 数码管的显示。
	
   wire key_pulse1;
	wire key_pulse2;
	wire key_pulse3;
	wire key_pulse4;
	wire key_pulse5;
	wire key_pulse6;
	// 对应于 6 个按键的按键消抖的输出线。
	
	reg state_of_start_or_nor = 0;
	// 判断是否开机了。
	
	reg really_start = 0;
	// 判断是否进行提醒吃药的功能了。
	
	reg [3:0] location = 1'd0;
	// 记录设置时间的时候的位置。
	
	wire clkout1;
	wire clkout2;
	wire clkout3;
	wire clkout4;
	// 有四个时钟分频,因此有四个输出。
	
	reg [1:0] led_position = 2'b11;
	// 判断是哪一个药盒需要进行闪烁以及蜂鸣器的提醒。
	
	reg [3:0] seg_data_1= 1'd0;
	reg [3:0] seg_data_2= 1'd0;
	// 记录没有正式工作的时候的数码管对应的需要显示的数字。
	
	reg [3:0] seg_data_10= 1'd0;
	reg [3:0] seg_data_20= 1'd0;
	// 记录正式开始工作以后的数码管对应的所需要显示的数字。
	
	reg [8:0] seg [10:0];      
	// 这个是数码管的显示部分内容,我们会在初始化的之后直接给它赋值。
	
	reg [5:0] first_medicine;
	reg [5:0] second_medicine;
	reg [5:0] third_medicine;
	// 存储服药的时间,因为有三个药箱,所以需要三个数据进行寄存。
	
	reg [5:0] counting;
	// 计时器的时间的记录,这个是一秒计一次数字。
	
	
	initial                                            
		begin
			seg[0] = 9'h3f;                                           
			// 对存储器中第一个数赋值9'b00_0011_1111,相当于共阴极接地,DP点变低不亮;
			// 7段显示数字  0。
	      seg[1] = 9'h06;                                           
			// 7段显示数字  1。
	      seg[2] = 9'h5b;                                           
			// 7段显示数字  2。
	      seg[3] = 9'h4f;                                           
			// 7段显示数字  3。
	      seg[4] = 9'h66;                                           
			// 7段显示数字  4。
	      seg[5] = 9'h6d;                                           
			// 7段显示数字  5。
	      seg[6] = 9'h7d;                                           
			// 7段显示数字  6。
	      seg[7] = 9'h07;                                           
			// 7段显示数字  7。
	      seg[8] = 9'h7f;                                           
			// 7段显示数字  8。
	      seg[9] = 9'h6f;                                           
			// 7段显示数字  9。
			
			seg[10] = 9'b0_0111_0110;
			// 显示H,表示说明是处于关机的状态,为什么是H呢,因为我的名字的首字母是H。
       end
	
	initial
		begin
			first_medicine <= 2'd0;
			second_medicine <= 2'd0;
			third_medicine <= 2'd0;
			// 让初始的三个计数都是零。
			
			counting <= 2'd00;
			
			times <= 0;
			times0 <= 0;
		end
	reg [63:0] mem___;
	
	// 设置点阵什么都不显示的状态。(初始状态,以及已经吃过药的状态。)
	initial
	begin
		mem___={8'b0000_0000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0000_0000};
					// 所有的点都处于熄灭的状态。
	end
	
	// Dot Matrix 有关的显示
	reg	[63:0]	mem;
	reg	[63:0]	mem1;
	reg	[63:0]	mem2;
	reg	[63:0]	mem3;
	// 开机了,但是没有正式开始工作时的闪烁现象。

	initial
	 begin
	 
	 
	 // 初始状态。
	 
	 
		mem1={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第一个灯
					// 所有的该亮的显示灯都凉亮了。
					
		mem2={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第二个灯
		mem3={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第三个灯
	end

	always@(posedge clk)
	begin
	// 通过时钟信号来控制,高电平的时候灯灭,低电平的时候灯亮,从而实现闪烁。
		if(clkout1)
		// 频率是 2 Hz
		
		begin
		
		
		// 熄灭状态。
		
		
			mem1={8'b0000_0000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第一个灯
		mem2={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第二个灯
		mem3={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0000_0000}; 
					// 第三个灯
		end
		
		else
		begin
		
		
		// 恢复状态。
		
		
			mem1={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第一个灯
		mem2={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第二个灯
		mem3={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第三个灯
		end
	end
	
	
	reg	[63:0]	mem0;
	reg	[63:0]	mem10;
	reg	[63:0]	mem20;
	reg	[63:0]	mem30;
	// 正式开始工作的时候的闪烁的现象。

	initial
	 begin
	 
	 
	 // 初始状态。
	 
	 
		mem10={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第一个灯
		mem20={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第二个灯
		mem30={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第三个灯
	end

	always@(posedge clk)
	begin
	// 也是更上面的那个一样的道理。
		if(clkout3)
		// 这个是 4 Hz 的频率。
		
		begin
		
		
		// 熄灭状态。
		
		
			mem10={8'b0000_0000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第一个灯
		mem20={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第二个灯
		mem30={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0000_0000, 
					8'b0000_0000}; 
					// 第三个灯
		end
		
		else
		begin
		
		
		// 恢复状态。
		
		
			mem10={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第一个灯
		mem20={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第二个灯
		mem30={8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000, 
					8'b0000_0000, 
					8'b0001_1000, 
					8'b0001_1000};
					// 第三个灯
		end
	end


	
	// 按键消抖,需要给使用到的 6 个按键都进行消抖,这里rst我们暂时没有用到,也许后续扩展的时候会使用。
	debounce_button  u1 (                               
					  .clk (clk),
					  .rst (rst),
					  .key (btn1),
					  .key_pulse (key_pulse1)
					  );
					  
	debounce_button  u2 (                               
					  .clk (clk),
					  .rst (rst),
					  .key (btn2),
					  .key_pulse (key_pulse2)
					  );
					  
	debounce_button  u3 (                               
					  .clk (clk),
					  .rst (rst),
					  .key (btn3),
					  .key_pulse (key_pulse3)
					  );
					  
	debounce_button  u4 (                               
					  .clk (clk),
					  .rst (rst),
					  .key (btn4),
					  .key_pulse (key_pulse4)
					  );
					  
	debounce_button  u5 (                               
					  .clk (clk),
					  .rst (rst),
					  .key (btn5),
					  .key_pulse (key_pulse5)
					  );
	
	debounce_button  u6 (                               
					  .clk (clk),
					  .rst (rst),
					  .key (btn6),
					  .key_pulse (key_pulse6)
					  );
					  
	// 时钟分频,我们需要一些不同的信号,所以需要不同的分频模块。
	time_split #(.N(6000000),
						.WIDTH(23)) t1 (
					.clk(clk),
					.rst_n(rst),
					.clkout(clkout1));
					// 频率为 2 Hz。
	
	time_split #(.N(12000000),
						.WIDTH(24)) t2 (
					.clk(clk),
					.rst_n(rst),
					.clkout(clkout2));
					// 频率为 1 Hz。
	
	time_split #(.N(3000000),
						.WIDTH(22)) t3 (
					.clk(clk),
					.rst_n(rst),
					.clkout(clkout3));
					// 频率为 4 Hz。
					
	time_split #(.N(1500000),
						.WIDTH(21)) t4 (
					.clk(clk),
					.rst_n(rst),
					.clkout(clkout4));
					// 频率为 8 Hz。
	
	// 显示Dot Matrix.
	
reg	[63:0] memory;
reg	[2:0]	state;

// 确定采用哪一种方法来对Dot Matrix进行显示。
always@(posedge clk)
begin

	if(state_of_start_or_nor && ~really_start)
	// 开机了,但是没有完全开始工作,这个时候是由 location 来进行决定的。
	
	begin
	// location.
		case(location)
			4'b0000:
			// 第一个位置
				begin
					mem <= mem1;
				end
				
			4'b0001:
			// 第二个位置
				begin
					mem <= mem2;
				end
				
			4'b0010:
			// 第三个位置
				begin
					mem <= mem3;
				end
				
			default:
			// 一共只有三个位置,因此这个也就是显示第三个位置
				begin
					mem <= mem3;
				end
				
			endcase
			
		end
		
		if(state_of_start_or_nor && really_start)
		// 开机了,而且是完全开始了。
		begin
		// led_position.
		
			case(led_position)
				2'b00:
				// 第一个位置
				
				begin
				if(btn_true_or_not1)
				// 如果吃过药了。
				begin
					mem0 <= mem___;
				end
				
				else
				begin
					mem0 <= mem10;
				end
				end
				
				2'b01:
				// 第二个位置
				
				begin
				if(btn_true_or_not2)
				// 如果吃过药了。
				begin
					mem0 <= mem___;
				end
				
				else
				begin
					mem0 <= mem20;
				end
				end
				
				2'b10:
				// 第三个位置
				
				begin
				if(btn_true_or_not3)
				// 如果吃过药了。
				begin
					mem0 <= mem___;
				end
				
				else
				begin
					mem0 <= mem30;
				end
				end
				
				default:
				// 已经吃过了所有的药了。
				
				begin
					mem0 <= mem___;
				end
				
			endcase
		end
		
		memory = state_of_start_or_nor ? (really_start ? mem0 : mem) : mem___;
		// 设置最后的输出的点阵。
		
end

always @(posedge clk_800hz or negedge rst)
// 扫描监测。
	if(!rst) begin state <= s0; col <= 8'hff; row = 8'hff; end
	// 如果按下了重置键以后的操作。
	
	else
	begin		
	// 采取扫描的形式进行状态的监测。
	
		case(state)
		// 共有 8 个状态,进行循环的扫描。
		
			s0:	begin col <= 8'b1111_1110; row = ~memory[56+:8]; state <= s1; end
			s1:	begin col <= 8'b1111_1101; row = ~memory[48+:8]; state <= s2; end
			s2:	begin col <= 8'b1111_1011; row = ~memory[40+:8]; state <= s3; end
			s3:	begin col <= 8'b1111_0111; row = ~memory[32+:8]; state <= s4; end
			s4:	begin col <= 8'b1110_1111; row = ~memory[24+:8]; state <= s5; end
			s5:	begin col <= 8'b1101_1111; row = ~memory[16+:8]; state <= s6; end
			s6:	begin col <= 8'b1011_1111; row = ~memory[ 8+:8]; state <= s7; end
			s7:	begin col <= 8'b0111_1111; row = ~memory[ 0+:8]; state <= s0; end
			default: begin state <= s0; col <= 8'hff; row = 8'hff; end
			
		endcase		
	end
	

				
	// 开机或者关机以及确定修改哪一个位置的药箱的提醒时间。			
	always@(posedge clk)
	// 时钟的上升沿到来。
		begin
		
			if(times_ && times0_)
			
			begin
				times <= 0;
			end
		
			if(key_pulse1)
			begin
				state_of_start_or_nor <= ~state_of_start_or_nor;
				
				times <= 1;
				// 如果发生了开关机,那么就把times置为一(1)// 如果按键 1 了,那么就需要翻转状态。
			end
			
			else
			begin
				state_of_start_or_nor <= state_of_start_or_nor;
				// 如果没有按键 1 ,状态保持不变。
			end
			
			if(state_of_start_or_nor && ~really_start)
			// 开机了,但是,并没有完全进入到工作状态的时候执行。
			begin
				if(key_pulse2)
				// 如果按下了按钮 2,那么就需要把设置时间的当前药盒后移一位。
				begin
					location <= (location + 1) % 3;
					// 总共有 3 个药盒,所以 % 3。
				end
				
				else
				begin
					location <= location;
					// 没有按键 2 的话,状态保持不变。
				end
				
				case(location)
				// 判断是哪一个药箱在设置时间。
				
					1'd0:begin
						led1 = clkout1;  // 闪烁。
						// 当前的位置需要,以 2Hz 的频率进行闪烁,我们让其他位置一直亮着不变。
						led2 = 0;
						led3 = 0;
					end
					
					1'd1:begin
						led1 = 0;
						led2 = clkout1;
						// 当前的位置需要,以 2Hz 的频率进行闪烁,我们让其他位置一直亮着不变。
						led3 = 0;
					end
					
					default:begin
					// 这个其实就是只能是最后一个位置了。
						led1 = 0;
						led2 = 0;
						led3 = clkout1;
						// 当前的位置需要,以 2Hz 的频率进行闪烁,我们让其他位置一直亮着不变。
					end
					
				endcase
			end
		end
		
//	always@(posedge clk or negedge rst)
//		begin
//			if(~rst)
//			begin
//				if(~state_of_start_or_nor && ~really_start)
//				begin
//						
//					if(counting == 0)
//					begin
//					end
//					else
//					begin
//						counting <= 0;
//					end
//					// 使得counting为0,这样子的话,下一次开始就也是从0开始了啦,目的是为了保证使用的合理性与方便性。
//				end
//			end
//		end
		
	// 通过按键来决定是否已经正式开始工作了,即就是判断是否开始提醒吃药的功能了。
	always@(posedge clk)
		begin
			if(state_of_start_or_nor)
			begin
				if(key_pulse5 && ~really_start)
				// 按键 5 是控制正式开始工作的。
				begin
					really_start <= 1;
//					times <= 1;
//					// 如果发生了正式开始工作,那么就把times置为一(1)//					if(counting == 0)
//					begin
//					end
//					else
//					begin
//						counting <= 0;
//					end
//					// 使得counting为0,这样子的话,下一次开始就也是从0开始了啦,目的是为了保证使用的合理性与方便性。
					//设置状态为正式开始工作了,即就是 really_start 由 0 变为了 1/**
					这个时候,由于已经正式开始工作了,
					于是,之前的切换药箱的位置,以及设置提醒的时间的操作就被禁止了。
					*/
					
				end
				
				if(key_pulse1 && really_start)
				begin
					really_start <= 0;
					
//					if(counting == 0)
//					begin
//					end
//					else
//					begin
//						counting <= 0;
//					end
//					// 使得counting为0,这样子的话,下一次开始就也是从0开始了啦,目的是为了保证使用的合理性与方便性。
					
					// 如果在正式工作的条件之下,按下了按键 1// 那么,就需要把正式工作的状态由 0 变为 1,这时,可以保证下一次开机,不会直接工作
				end
			end
		end

	// 正式开始工作以后的计数操作。
	reg btn_true_or_not1 = 0;
	reg btn_true_or_not2 = 0;
	reg btn_true_or_not3 = 0;
	// 这三个变量是用来判断是否已经吃过药了的。
	
	always@(posedge clkout2)
	// 每个一秒钟都会加一次数字,相当于一秒加一次一,一秒加一的计数器啦。
		begin
			if(state_of_start_or_nor && really_start)
			// 如果已经开机了而且是在已经处于正式开始工作了的状态了。
			
			begin
			
				counting <= (counting + 1) % 60;
				// 计数。
				
				if(times_)
				// 如果发生了开机以及关机的相应的操作,就需要执行以下的代码。
				
				begin
					counting <= 2'd00;
					// 如果发生了开机以及关机的相应的操作,就需要把counting置为零。
					seg_data_10 <= 0;
					seg_data_20 <= 0;
					
					times0 <= 1;
					
					// 为了防止一直处于置零的状态,这里必须把 times 置零,这里是表示进行了置零的操作。
				end
				
				else
				begin
					if(times0_)
					begin
						times0 <= 0;
						// 置零,表示整个交互过程的结束。
						
					end
				end
				
//				counting <= (counting + 1) % 60;
//				// 计数。
				
				// 计算表达的数字应该是多少。
				seg_data_10 <= counting / 10;
				if(counting % 10 == 0)
				begin 
					seg_data_20 <= 0;
				end
				else
				begin 
					seg_data_20 <= counting - 10 * seg_data_10;
					// 这里与之前的其实是一样的了啦。
				end
				
				// 记录究竟是哪一个药箱该拿取药物了。
				if(counting >= 0 && counting < first_medicine)
					begin
						led_position = 2'b11;
						// 药盒 123 都没有达到取药的条件啦,不取药,因此是不需要提醒的状态。
					end
					
				if(counting >= first_medicine && counting < second_medicine)
					begin
						led_position = 2'b00;
						// 药盒 1 需要进行取药。
					end
					
				if(counting >= second_medicine && counting < third_medicine)
					begin
						led_position = 2'b01;
						// 药盒 2 需要取药。
					end
					
				if(counting >= third_medicine && counting < 61)
					begin
						led_position = 2'b10;
						// 药盒 3 需要取药。
					end
			end
		end
		
	// 由于计时器和闪烁提醒的时间周期不一样,所以说,我们需要把这两个给它独立起来操作,前一个是进行计数器的操作。
	// 这里进行闪烁提醒的操作。
	always@(posedge clk)
		begin
			if(state_of_start_or_nor && really_start)
			// 如果说是,已经开机了,而且是正式的进入了工作的状态了,就开始执行下面的执行的代码了啦。
			begin
			
				case(led_position)
					// 如果说是第一个药盒该开始提醒吃药的 。
				
					2'b00:begin
					
					
						if(btn_true_or_not3)
						
						begin
							btn_true_or_not3 = 0;
							//0 。
						end
						// 将前一个已经吃过药的状态设置为 0 ,即就是设置上一个 3 为未吃药的状态。
						
						
						if(~btn_true_or_not1)
						// 如果没有吃药。
						
						begin
							led10 = clkout3;
							led20 = 1;
							led30 = 1;
							// 该吃药的对应的那个药盒的等闪烁,其他的处于熄灭的状态。
							
							led40 = clkout4;
							// 蜂鸣器。
							
							if(key_pulse6)
							// 按下了按键 6 ,表示吃药了。
							
							begin
								btn_true_or_not1 = 1;
								led40 = 1;
								// 如果已经吃过药了,那么,设置吃药的状态为 1 ,而且蜂鸣器不再响了。
							end
						end
						
						else
						begin
							led10 = 1;
							led20 = 1;
							led30 = 1;
							led40 = 1;
							// 虽然前面已经设置过了,但是在写一遍不影响的额。
						end
					end
					
					// 第二个药盒该吃药了。
					
					2'b01:begin
					
					
						if(btn_true_or_not1)
						
						begin
							btn_true_or_not1 = 0;
							//0 。
						end
						// 将前一个已经吃过药的状态设置为 0 ,即就是设置上一个 1 为未吃药的状态。
						
						
						if(~btn_true_or_not2)
						// 如果没有吃药。
						
						begin 
							led10 = 1;
							led20 = clkout3;
							led30 = 1;
							// 该吃药的对应的那个药盒的等闪烁,其他的处于熄灭的状态。
							
							led40 = clkout4;
							// 蜂鸣器。
							
							if(key_pulse6)
							// 按下了按键 6 ,表示吃药了。
							
							begin
								btn_true_or_not2 = 1;
								led40 = 1;
								// 如果已经吃过药了,那么,设置吃药的状态为 1 ,而且蜂鸣器不再响了。
							end
						end
						
						else
						begin
							led10 = 1;
							led20 = 1;
							led30 = 1;
							led40 = 1;
							// 虽然前面已经设置过了,但是在写一遍不影响的额。
						end
					end
					
					// 第三个药盒该吃药了。
					
					2'b10:begin
					
					
						if(btn_true_or_not2)
						
						begin
							btn_true_or_not2 = 0;
							//0 。
						end
						// 将前一个已经吃过药的状态设置为 0 ,即就是设置上一个 2 为未吃药的状态。
						
						
						if(~btn_true_or_not3)
						// 如果没有吃药。
						
						begin
							led10 = 1;
							led20 = 1;
							led30 = clkout3;
							// 该吃药的对应的那个药盒的等闪烁,其他的处于熄灭的状态。
							
							led40 = clkout4;
							// 蜂鸣器。
							
							if(key_pulse6)
							// 按下了按键 6 ,表示吃药了。
							
							begin
								btn_true_or_not3 = 1;
								led40 = 1;
								// 如果已经吃过药了,那么,设置吃药的状态为 1 ,而且蜂鸣器不再响了。
							end
						end
						
						else
						begin
							led10 = 1;
							led20 = 1;
							led30 = 1;
							led40 = 1;
							// 虽然前面已经设置过了,但是在写一遍不影响的额。
						end
					end
					
					default:begin
					// 其他的情况,也就是说,不需要吃药的情况啦。
					
						led10 = 1;
						led20 = 1;
						led30 = 1;
						led40 = 1;
					end
				endcase
				end
		end
		
	// 在开机之后进行设置三个药箱提醒吃药的时间。	
	always@(posedge clk)
		begin
			if(state_of_start_or_nor && ~really_start)
			// 这里也是开机了,但是没有正式开始工作。
			begin
				
//				counting <= 0;
//				// 使得counting为0,这样子的话,下一次开始就也是从0开始了啦,目的是为了保证使用的合理性与方便性。
			
				case(location)
				
					1'd0:begin
						if(key_pulse3)
						begin
							first_medicine <= (first_medicine + 1) % 60;
							// 如果按下了 3 按键,那么,时间加一。
						end
						
						else 
						begin
							if(key_pulse4)
							begin
								first_medicine <= (first_medicine -1 + 60) % 60;
								// 防止减出负数来了。
								// 如果按下了 4 按键,那么,时间减一。
							end
							
							else
							begin
								first_medicine <= first_medicine;
								// 如果 34都没有按的话,那么,就保持不变。
							end
						end
						
						seg_data_1 <= first_medicine / 10;
						// 10位数字。
						if(first_medicine % 10 == 0)
						begin 
							seg_data_2 <= 0;
							// 本来没有必要这么操作,但不知道为啥两种情况综合在一起就无法显示0,所以才分开来了。
						end
						else
						begin 
							seg_data_2 <= first_medicine - 10 * seg_data_1;
						end
						// 个位数字。
					end
					
					1'd1:begin
						if(key_pulse3)
						begin
							second_medicine <= (second_medicine + 1) % 60;
							// 如果按下了 3 按键,那么,时间加一。
						end
						
						else 
						begin
							if(key_pulse4)
							begin
								second_medicine <= (second_medicine -1 + 60) % 60;
								// 如果按下了 4 按键,那么,时间减一。
							end
							else
							begin
								second_medicine <= second_medicine;
								// 如果 34都没有按的话,那么,就保持不变。
							end
						end
						
						// 计算两位数字分别是多少。
						seg_data_1 <= second_medicine / 10;
						if(second_medicine % 10 == 0)
						begin 
							seg_data_2 <= 0;
						end
						else
						begin 
							seg_data_2 <= second_medicine - 10 * seg_data_1;
						end
					end
					
					default:begin
					// 这个其实就是位置 3 了。
						if(key_pulse3)
						begin
							third_medicine <= (third_medicine + 1) % 60;
							// 如果按下了 3 按键,那么,时间加一。
						end
						else 
						begin
							if(key_pulse4)
							begin
								third_medicine <= (third_medicine -1 + 60) % 60;
								// 如果按下了 4 按键,那么,时间减一。
							end
							else
							begin
								third_medicine <= third_medicine;
								// 如果 34都没有按的话,那么,就保持不变。
							end
						end
						
						// 计算两位数字分别是多少。
						seg_data_1 <= third_medicine / 10;
						if(third_medicine % 10 == 0)
						begin 
							seg_data_2 <= 0;
						end
						else
						begin 
							seg_data_2 <= third_medicine - 10 * seg_data_1;
						end
					end
					
				endcase
			end
			else
			begin 
			end
		end
	
	assign seg_led_1 = state_of_start_or_nor ? (really_start?seg[seg_data_10]:seg[seg_data_1]) : seg[10];                         
	assign seg_led_2 = state_of_start_or_nor ? (really_start?seg[seg_data_20]:seg[seg_data_2]) : seg[10];
	// 数码管的显示。
	
	assign rled1=state_of_start_or_nor ? (really_start?led10:led1) :1;       
	assign rled2=state_of_start_or_nor ? (really_start?led20:led2) :1;  
	assign rled3=state_of_start_or_nor ? (really_start?led30:led3) :1;
	assign rled4=state_of_start_or_nor ? (really_start?led40:led4) :1;
	// 总共有三种状态,未开机,开机,正式工作,因此需要有三种方式来进行显示提醒以及时间的内容啦。
		
endmodule

2、主文件中使用的按键消抖模块
// ********************************************************************
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// ********************************************************************
// File name    : debounce.v
// Module name  : debounce
// Author       : STEP
// Description  : 
// Web          : www.stepfpga.com
// 
// --------------------------------------------------------------------
// Code Revision History : 
// --------------------------------------------------------------------
// Version: |Mod. Date:   |Changes Made:
// V1.0     |2017/03/02   |Initial ver
// --------------------------------------------------------------------
// Module Function:按键消抖
 
module debounce_button (clk,rst,key,key_pulse);
 
        parameter       N  =  1;                      //要消除的按键的数量
 
	input             clk;
        input             rst;
        input 	[N-1:0]   key;                        //输入的按键					
	output  [N-1:0]   key_pulse;                  //按键动作产生的脉冲	
 
        reg     [N-1:0]   key_rst_pre;                //定义一个寄存器型变量存储上一个触发时的按键值
        reg     [N-1:0]   key_rst;                    //定义一个寄存器变量储存储当前时刻触发的按键值
 
        wire    [N-1:0]   key_edge;                   //检测到按键由高到低变化是产生一个高脉冲
 
        //利用非阻塞赋值特点,将两个时钟触发时按键状态存储在两个寄存器变量中
        always @(posedge clk  or  negedge rst)
          begin
             if (!rst) begin
                 key_rst <= {N{1'b1}};                //初始化时给key_rst赋值全为1{}中表示N个1
                 key_rst_pre <= {N{1'b1}};
             end
             else begin
                 key_rst <= key;                     //第一个时钟上升沿触发之后key的值赋给key_rst,同时key_rst的值赋给key_rst_pre
                 key_rst_pre <= key_rst;             //非阻塞赋值。相当于经过两个时钟触发,key_rst存储的是当前时刻key的值,key_rst_pre存储的是前一个时钟的key的值
             end    
           end
 
        assign  key_edge = key_rst_pre & (~key_rst);//脉冲边沿检测。当key检测到下降沿时,key_edge产生一个时钟周期的高电平
 
        reg	[17:0]	  cnt;                       //产生延时所用的计数器,系统时钟12MHz,要延时20ms左右时间,至少需要18位计数器     
 
        //产生20ms延时,当检测到key_edge有效是计数器清零开始计数
        always @(posedge clk or negedge rst)
           begin
             if(!rst)
                cnt <= 18'h0;
             else if(key_edge)
                cnt <= 18'h0;
             else
                cnt <= cnt + 1'h1;
             end  
 
        reg     [N-1:0]   key_sec_pre;                //延时后检测电平寄存器变量
        reg     [N-1:0]   key_sec;                    
 
 
        //延时后检测key,如果按键状态变低产生一个时钟的高脉冲。如果按键状态是高的话说明按键无效
        always @(posedge clk  or  negedge rst)
          begin
             if (!rst) 
                 key_sec <= {N{1'b1}};                
             else if (cnt==18'h3ffff)
                 key_sec <= key;  
          end
       always @(posedge clk  or  negedge rst)
          begin
             if (!rst)
                 key_sec_pre <= {N{1'b1}};
             else                   
                 key_sec_pre <= key_sec;             
         end      
       assign  key_pulse = key_sec_pre & (~key_sec);     
 
endmodule

3、主文件中使用的时钟分频模块
// ********************************************************************
// >>>>>>>>>>>>>>>>>>>>>>>>> COPYRIGHT NOTICE <<<<<<<<<<<<<<<<<<<<<<<<<
// ********************************************************************
// File name    : divide.v
// Module name  : divide
// Author       : STEP
// Description  : clock divider
// Web          : www.stepfpga.com
// 
// --------------------------------------------------------------------
// Code Revision History : 
// --------------------------------------------------------------------
// Version: |Mod. Date:   |Changes Made:
// V1.0     |2017/03/02   |Initial ver


// --------------------------------------------------------------------











// Module Function:任意整数时钟分频
 
 
module time_split#(parameter	WIDTH	= 24, parameter	N	= 12000000)(clk,rst_n,clkout);
 
        input 	clk,rst_n;                       //输入信号,其中clk连接到FPGA的C1脚,频率为12MHz
        output	clkout;                          //输出信号,可以连接到LED观察分频的时钟
 
        //parameter是verilog里常数语句
//	parameter	WIDTH	= 24;             //计数器的位数,计数的最大值为 2**WIDTH-1
//	parameter	N	= 12000000;             //分频系数,请确保 N < 2**WIDTH-1,否则计数会溢出
// 
	reg 	[WIDTH-1:0]	cnt_p,cnt_n;     //cnt_p为上升沿触发时的计数器,cnt_n为下降沿触发时的计数器
	reg			clk_p,clk_n;     //clk_p为上升沿触发时分频时钟,clk_n为下降沿触发时分频时钟
 
	//上升沿触发时计数器的控制
	always @ (posedge clk or negedge rst_n )         //posedge和negedge是verilog表示信号上升沿和下降沿
                                                         //当clk上升沿来临或者rst_n变低的时候执行一次always里的语句
		begin
			if(!rst_n)
				cnt_p<=0;
			else if (cnt_p==(N-1))
				cnt_p<=0;
			else cnt_p<=cnt_p+1;             //计数器一直计数,当计数到N-1的时候清零,这是一个模N的计数器
		end
 
         //上升沿触发的分频时钟输出,如果N为奇数得到的时钟占空比不是50%;如果N为偶数得到的时钟占空比为50%
         always @ (posedge clk or negedge rst_n)
		begin
			if(!rst_n)
				clk_p<=0;
			else if (cnt_p<(N>>1))          //N>>1表示右移一位,相当于除以2去掉余数
				clk_p<=0;
			else 
				clk_p<=1;               //得到的分频时钟正周期比负周期多一个clk时钟
		end
 
        //下降沿触发时计数器的控制        	
	always @ (negedge clk or negedge rst_n)
		begin
			if(!rst_n)
				cnt_n<=0;
			else if (cnt_n==(N-1))
				cnt_n<=0;
			else cnt_n<=cnt_n+1;
		end
 
        //下降沿触发的分频时钟输出,和clk_p相差半个时钟
	always @ (negedge clk)
		begin
			if(!rst_n)
				clk_n<=0;
			else if (cnt_n<(N>>1))  
				clk_n<=0;
			else 
				clk_n<=1;                //得到的分频时钟正周期比负周期多一个clk时钟
		end
 
        assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p;      //条件判断表达式
                                                                    //当N=1时,直接输出clk
                                                                    //当N为偶数也就是N的最低位为0,N(0=0,输出clk_p
                                                                    //当N为奇数也就是N最低位为1,N(0=1,输出clk_p&clk_n。正周期多所以是相与
endmodule     

三、后续操作

编译
在这里插入图片描述

设置引脚
(注意设置引脚以后需要重新进行编译才可以实现修改项目的操作。)

在这里插入图片描述
在这里插入图片描述

烧录
在这里插入图片描述

四、实验现象

我们仍然采用视频的形式展示实验的现象:

https://www.bilibili.com/video/BV1MY411x7Xw?spm_id_from=333.999.0.0

Verilog HDL实现智能药盒

五、总结

以上就是使用Verilog HDL来实现智能药盒的全部流程,希望对大家有一些帮助啦。

最后,谢谢大家的阅读与支持了啦。

原创声明:该设计来自机智云,设计资料仅供学习参考,不可以用于商业用途 老人吃药智能提醒器功能概述: “哎呀,忘了”!随着生活节奏的加快,年龄的增长,人们的总容易忘记一些事情,比如忘记吃药。尤其是家中的老人,由于身体问题,可能需要长期服药治疗,但年岁大了总容易忘这忘那的。但在科技快速发展的今天,这些都不是问题,我们设计并开发了一款名为“健康卫士”的智能药盒,可以联网监测用户的服药情况,提示用户服药,并且根据用户设定自动提取所需药物。 硬件系统设计: 硬件系统主要是pcb 走线布局 +stm32(C语言 功能实现)+wifi(esp8266)+机智云平台 软件系统 软件部分主要是用机智云的sdk,集成了机智云的sdk,开发起来还是很好入门的,向我这种小白在学了两个星期后就基本入门了,加上自己做的UI就可以了。代码在附件内容。 总体结构: 电气部分:通过重新设计板子设计基于机智云平台的设备,设计集成了若干模块,主要包括eeprom,时钟模块、运动驱动模块、语音提醒模块、传感器模块(温湿度、霍尔、光耦)、网络模块。 药盒模型:重点解决自动提取药物部分的模型(转盘式双层筛板) 云:使用机智云平台,独立搭建数据点。 APP:与机智云平台和MCU通讯,从而进行对智能药盒的管理控制;远程监控;远程提醒,远程温湿度查看。 发挥项设计: 1 语音播报 通过wt588d, 进行语音提醒 ,感情切(也可以自己录音哦) 2 断电后数据不丢 通过数据保存(一次设定,全程无忧)。(再也不用担心重复设定了) 3 自动化分拣药品种类 每天还在想那个药品吃多少吗(你就out 啦 ,全新智能药盒, 再也不用烦恼了 ) 4 断网后自动连接 断网后重新设置 不用 (网络连接失败后(有网络后)会重新连接的)
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

hhh江月

您的鼓励将是我创作的最大动力。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值