基于GD32VF103 的vga显示器 和ps2键盘 驱动

基于GD32VF103的vga和ps2键盘驱动

前言

gd32vf103 是国内一款很不错的riscv架构微处理器,但是网上gd32vf103的应用还比较少,这里我决定分享一下利用这个微处理器制作的vga驱动和ps2键盘驱动的调试过程和思路,主要还是用来学习定时器、spi等这些单片机常用的功能,希望能帮到有需要的人 源码开源到了我的github上有需要自取gd32vf103 vga&ps2keyboard

PS2 键盘驱动

ps2 键盘在几年前还是非常多人使用,我现在仍然用的ps2的键盘鼠标 ,这里选用ps2 作为人机交互主要原因是它的接口和时序都非常简单

ps2 硬件连接

ps2 接口电路如图ps2接口
我们这里用到了四根线,大致的连接是这样

PS2PIN -> GD32vf103PIN
4 -> 5v
3 -> GND
5(clk)->PA0
1(data) -> PA3

ps2 时序

我们这里单片机作为从机接收ps2键盘发来的数据,所以简单来说就是在clk下降沿读取data的电平。首先要将clk和data拉高,不然键盘不会发送数据。在这里插入图片描述
第一位是start 0, 最后一位是stop 1,倒数第二位校验,所以说我们要的数据就在中间data0-data7 一个移位就能读出
我们要做的就是在clk对应的引脚(PA0)挂上下降沿的外部中断,然后在中断处理函数中读取data对应引脚(PA3)电平,读取的函数如下

void EXTI0_IRQHandler(void) {
	u8 state_now;
	ps2_check = 0;
	if (RESET != exti_interrupt_flag_get(EXTI_0)) {
		state_now = gpio_input_bit_get(GPIOA, PS2_DATA);
		if (ps2_bit_count == 11) {
			ps2_data_now = 0x00;
			if (0 == state_now) {
				ps2_bit_count--;
			}
		} else {
			if (ps2_bit_count < 11 && ps2_bit_count > 2) {
				ps2_data_now = (ps2_data_now >> 1);
				if (0 != state_now) {
					ps2_data_now = ps2_data_now + 0x80;
				}
				ps2_bit_count--;
			} else {
				if (ps2_bit_count == 2) {
					ps2_bit_count--;
				} else if (ps2_bit_count == 1) {
					ps2_bit_count = 11;
					u8 current_char = ps2_decode(ps2_data_now);
					if (current_char != 0) {
						set_char(current_char);
					}
				}
			}
		}
	}
	exti_interrupt_flag_clear(EXTI_0);
	//GPIO_BC(GPIOA) = LED_B;
	//printf("in handler:%d  now%d\n", ps2_bit_count,state_now );
}

然后就是数据的处理了,我这里用的第二套键盘扫描码,通码如下 在这里插入图片描述
按键按下发送通码,弹起发送断码,因为我周边还有很多没搭好,所以这里就简单处理让检测到f0后接收到的码作为当前按键,emmm确实这里目前没做其他的比如shift之类的,不过也很简单,隔段时间来填这个坑吧。

vga驱动

由于单片机的算力有限,为了还能干些其他事情 ,我们这里使用单色 分辨率是800* 600@56Hz ,同样考虑到这只是用来刷字符的卡 实际分辨率是600* 300

硬件接口

我们用的15针vga 大致连接如下

VGAPIN -> GD32VF103
HSYNC ->PB0
VSYNC -> PB6
GREEN->PA7

在这里插入图片描述
red和blue就直接接地

vga 时序以及驱动实现

具体单片机实现推荐去看看国外这个大神的,他是利用stm32f1完成,我在实现中也参照了他的一些思路。有关vga时序也是百度一大把这里就先不多介绍
stm32f1 vga驱动

我使用TIMER2CH2 作为行同步信号 通道三作为计算了消影的后的时间,即开始刷像素点的信号
在timer2中断处理函数中计算行数来进行场同步,这里更好的是利用定时器的主从模式让timer2来触发timer3,我为了节约定时器采用的在中断中记时并产生场同步信号,实测效果也不错,大致的初始化这样

void vga_timer_config(void) {
	timer_oc_parameter_struct timer_ocinitpara;
	timer_parameter_struct timer_initpara;

	rcu_periph_clock_enable(RCU_TIMER2);
	//rcu_periph_clock_enable(RCU_TIMER3);
	timer_deinit(TIMER2);
	timer_struct_para_init(&timer_initpara);
	timer_initpara.prescaler = 0;
	timer_initpara.alignedmode = TIMER_COUNTER_EDGE;
	timer_initpara.counterdirection = TIMER_COUNTER_UP;
	timer_initpara.period = 3072;
	timer_initpara.clockdivision = TIMER_CKDIV_DIV1;
	timer_initpara.repetitioncounter = 0;
	timer_init(TIMER2, &timer_initpara);

	timer_channel_output_struct_para_init(&timer_ocinitpara);
	timer_ocinitpara.outputstate = TIMER_CCX_ENABLE;
	timer_ocinitpara.outputnstate = TIMER_CCXN_DISABLE;
	timer_ocinitpara.ocpolarity = TIMER_OC_POLARITY_HIGH;
	timer_ocinitpara.ocnpolarity = TIMER_OCN_POLARITY_HIGH;
	timer_ocinitpara.ocidlestate = TIMER_OC_IDLE_STATE_LOW;
	timer_ocinitpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;

	timer_channel_output_config(TIMER2, TIMER_CH_2, &timer_ocinitpara);

	timer_channel_output_config(TIMER2, TIMER_CH_3, &timer_ocinitpara);

	timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_2, 216);
	timer_channel_output_mode_config(TIMER2, TIMER_CH_2,
	TIMER_OC_MODE_PWM1);
	timer_channel_output_shadow_config(TIMER2, TIMER_CH_2,
	TIMER_OC_SHADOW_DISABLE);

	timer_channel_output_pulse_value_config(TIMER2, TIMER_CH_3, 512);
	timer_channel_output_mode_config(TIMER2, TIMER_CH_3,
	TIMER_OC_MODE_PWM1);
	timer_channel_output_shadow_config(TIMER2, TIMER_CH_3,
	TIMER_OC_SHADOW_DISABLE);

	//timer_auto_reload_shadow_enable(TIMER2);
	//timer_master_slave_mode_config(TIMER3, TIMER_MASTER_SLAVE_MODE_ENABLE);
	timer_interrupt_flag_clear(TIMER2, TIMER_INT_CH2);
	timer_interrupt_flag_clear(TIMER2, TIMER_INT_CH3);
	timer_interrupt_enable(TIMER2,
	TIMER_INT_CH2 | TIMER_INT_CH3);

	timer_enable(TIMER2);

}

由上面的分辨率算出来我们的像素时钟频率应该是36mhz,加上消影时间就得到了定时器的分频和具体的pulse

疫情在家条件有限,没有示波器和逻辑分析仪

我们刷像素点使用的是spi-dma功能,即dma自动加载像素点中数据到spi缓冲区发送,由于分频限制我们做的4分频,即时钟频率是27mhz 同时将每行数据刷两次。即我们实际的分辨率是600* 300,不高,但是单片机来说足够了,这里注意重新转载新的一行时要先将dma失能,然后再在开始传输时使能dma通道

具体实现还是直接看我GitHub代码吧 ,这里放几张成功实现的图
在这里插入图片描述
这是满屏绿色的

在这里插入图片描述
由于家中条件有限,连接只有杜邦线面包板,面包板寄生电容和线上干扰很大,所以说显示也有点丑陋,等打板后再继续吧
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
欢迎大家来交流 邮箱 :chen.yuheng@nexuslink.cn

打板出来了,很尴尬的是定义的按键在boot上,插上去没法dfu烧录。。。在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
可以多个大小字体切换,改变填充方式后也更漂亮了。同时移植了mathbasic解释器上去
tiny-math-basic解释器

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值