51开源 USB 三键摸鱼 键盘@STC32G, 或者自己改成STC8H8K64U
51开源 USB 三键摸鱼 键盘@STC32G, 或者自己改成STC8H8K64U成品如上图所示!话不多说开始介绍功能!开源程序源代码见附件一、功能介绍:单机版键盘的按键功能如下图所示,一共有3+6个按键,正面的三个按键就是机械键盘的可插拔键轴,侧面六个按键(上三个,下三个)是侧贴的沉板按键.┌────键4-───键5-───键6-───┐
│ ┌───┐┌───┐┌───┐ │
│ │键1││键2││键3│ C <=== 连接数据线的TYPEC口
│ └───┘└───┘└───┘ │
└────键7-───键8-───键9-───┘
默认按键参数:
键4 = 数字1
键5 = 数字2
键6 = 数字3
键7 = 数字4
键8 = 数字5
键9 = 数字6
键1 = CTRL+C
键2 = CTRL+V
键3 = ALT+TAB
另外板子上还有三个全彩WS2812的LED,驱动代码已经写好(基于DMA-SPI驱动,有效减少cpu占用),和一个PWM驱动的无缘蜂鸣器!
二、代码讲解:
开始代码前自行预习前面的知识:1.USB基础知识:https://www.stcaimcu.com/forum.php?mod=viewthread&tid=38992.USB复合设备(CDC+HID)分析:https://www.stcaimcu.com/forum.php?mod=viewthread&tid=4080
基于前面的两个章节,已经可以实现设备插入电脑识别出USB CDC和HID设备了,那么后面就很简单了!(1)CDC数据处理:
void usb_out_ep3()
{
BYTE csr;
BYTE cnt;
u8 dat;
usb_write_reg(INDEX, 3);
csr = usb_read_reg(OUTCSR1);
if (csr & OUTSTSTL)
{
usb_write_reg(OUTCSR1, OUTCLRDT);
}
if (csr & OUTOPRDY)
{
cnt = usb_read_reg(OUTCOUNT1);
while (cnt)
{
CDC_RxBuffer =usb_read_reg(FIFO3);
cnt--;
}
CDC_RxBuffer= '\0';
if(strcmp(CMD_ReadParm,CDC_RxBuffer)==0)
{
USBCON = 0x00; //清除USB设置11111111
USBCLK = 0x00;
IRC48MCR = 0x00;
Delay20ms();
IAP_CONTR = 0x60; //触发软件复位,从ISP开始执行
}
if (RxWptr - RxRptr >= 256 - EP1OUT_SIZE)
{
UsbOutBusy = 1;
}
else
{
usb_write_reg(OUTCSR1, 0);
}
}
}
因为CDC占用的端点3,所以只需要在端点3的接收函数里处理下数据,这里将接收到的数据保存在CDC_RxBuffer里,数据个数保存在CDC_RxBuffer_cnt里,当然这里其实还有一个小技巧,为了配合上位机实现不停电下载,这里还加了一段字符串比较函数,如果接受到的字符串是"@STCISP#"就会进入下载模式!!只需要配合ISP软件的这个即可实现不停电下载!!
当然这个CDC最主要的功能当然不是只用一个不停电下载,是为了后面的上位机修改按键键码和自定义灯光特效!(既然都讲到了,那就提前放个上位机的半成品图片预告一下咯,还没做完,界面有点丑)
上面是接收处理,这里由于数据不处理,仅供测试,那么这里直接把接收到的数据直接回传到上位机,这里就可以在主函数里添加如下内容“
if((CDC_RxBuffer_cnt>0)&&( !UsbInBusy)) //如果CDC有接受到数据
{
CDC_SendData(CDC_RxBuffer,CDC_RxBuffer_cnt); //CDC串口发回数据
CDC_RxBuffer_cnt = 0; //清空数据
}
这里判断哪个按键按下之后通过KEYNUM_Read函数读取内部的按键键码数据(因为后面会通过上位机将键码数据保存到内部EEPROM,所以这里提前编写好了一个接口便于后期直接替换),这里的键码数据如下如所示
u8 KEYNUM0 = { 0X00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; //空白
u8 KEYNUM1 = { 0X01,0x00,0x06,0x00,0x00,0x00,0x00,0x00 }; //复制
u8 KEYNUM2 = { 0X01,0x00,0x19,0x00,0x00,0x00,0x00,0x00 }; //粘贴
u8 KEYNUM3 = { 0X04,0x00,0x2b,0x00,0x00,0x00,0x00,0x00 }; //界面切换
u8 KEYNUM4 = { 0X00,0x00,0x1E,0x00,0x00,0x00,0x00,0x00 }; //数字1
u8 KEYNUM5 = { 0X00,0x00,0x1F,0x00,0x00,0x00,0x00,0x00 }; //数字2
u8 KEYNUM6 = { 0X00,0x00,0x20,0x00,0x00,0x00,0x00,0x00 }; //数字3
u8 KEYNUM7 = { 0X00,0x00,0x21,0x00,0x00,0x00,0x00,0x00 }; //数字4
u8 KEYNUM8 = { 0X00,0x00,0x22,0x00,0x00,0x00,0x00,0x00 }; //数字5
u8 KEYNUM9 = { 0X00,0x00,0x23,0x00,0x00,0x00,0x00,0x00 }; //数字6
装填好了八个数据的键码,这里就可以开始准备发送数据了,发送之前
1.判断端口是否空闲
2.如果空闲先给他置忙碌
3.选择对应的USB端点(这里是端点1)
4.8个字节的数据写入该端点的缓存
5.开启发送
具体的代码见上面!
当然这里为什么键码是八个字节的呢!
USB键盘数据包含8个字节,他们所代表的数据分别如下:
所以,这里列举两个例子1.发送数值1的键码(BYTE2写入0X1E,这个数据的由来见代码包里的HID码表):0X00,0x00,0x1E,0x00,0x00,0x00,0x00,0x002.发送CTRL+c的键码(BYTE0写入0x01,BYTE2写入0X1E,这个数据的由来见代码包里的HID码表):0X01,0x00,0x06,0x00,0x00,0x00,0x00,0x003.发送1+2+3的键码:0X00,0x00,0x1e,0x1f,0x20,0x00,0x00,0x00
以此类推@
(3)WS2812数据处理:
相信很多小伙伴都有做WS2812的时候,但是很多人还是用的延时,但是延时的时候一旦某个中断响应一下就必死无疑了,这里用一种SPI+DMA的方式实现基本0占用时间的方式1.配置SPIvoid SPI_init(void)
{
P_SW1 = (P_SW1 & ~(3<<2)) | (3<<2); //IO口切换. 0: P1.2/P5.4 P1.3 P1.4 P1.5, 1: P2.2 P2.3 P2.4 P2.5, 2: P5.4 P4.0 P4.1 P4.3, 3: P3.5 P3.4 P3.3 P3.2
SSIG = 1; //忽略 SS 引脚功能,使用 MSTR 确定器件是主机还是从机
SPEN = 1; //使能 SPI 功能
DORD = 0; //先发送/接收数据的高位( MSB)
MSTR = 1; //设置主机模式
CPOL = 0; //SCLK 空闲时为低电平,SCLK 的前时钟沿为上升沿,后时钟沿为下降沿
CPHA = 0; //数据 SS 管脚为低电平驱动第一位数据并在 SCLK 的后时钟沿改变数据
SPCTL = (SPCTL & ~3) | 3; //SPI 时钟频率选择, 0: 4T, 1: 8T,2: 16T,3: 2T
P32=P33=P34=0; //端口电平设置为低电平
HSCLKDIV = 0x01; //高速时钟8分频,默认2分频。开漏模式通过10K电阻上拉到3.3V,电平上升速度慢,需要降低SPI速率才能正常通信。
}
2.配置DMAvoid DMA_Config(void)
{
DMA_SPI_STA = 0x00;
DMA_SPI_CFG = 0xE0; //bit7 1:Enable Interrupt
DMA_SPI_AMT = 0xff; //设置传输总字节数:n+1
DMA_SPI_TXAH = (u8)((u16)&DmaTxBuffer >> 8); //SPI发送数据存储地址
DMA_SPI_TXAL = (u8)((u16)&DmaTxBuffer);
DMA_SPI_RXAH = (u8)((u16)&DmaRxBuffer >> 8); //SPI接收数据存储地址
DMA_SPI_RXAL = (u8)((u16)&DmaRxBuffer);
DMA_SPI_CFG2 = 0x01; //01:P2.2
DMA_SPI_CR = 0x81; //bit7 1:使能 SPI_DMA, bit6 1:开始 SPI_DMA 主机模式, bit0 1:清除 SPI_DMA FIFO
}
这里其实只需要发送数据,所以接收的不配置也可以。
实际测试在24MHZ的情况下,这个SPI发出的数据为F0就是0码的电平,FF就是1码的电平!关于这个电平和WS2812驱动原理的描述可以看帖子:https://www.stcaimcu.com/forum.php?mod=viewthread&tid=292
有了上面的基础,剩下的是需要把颜色数据写入缓存,让DMA自己去刷新就好了!这样24个字节就是一个灯的颜色数据,DMA控制spi发出24个数据就能点亮一个灯,且24色数据就是 绿,红,蓝的数据,这里先调用一个函数将颜色数据写入缓存:void WS_Write(u8 num,u8 g,u8 r,u8 b)
{
u16 i;
num = num -1;
color = ((((u32)g)<<16)+(((u32)r)<<8)+(((u32)b)<<0)); //当前的颜色数值写入数组
for(i=0;i<8;i++) //循环八次,先将绿色的数值写入缓存数组
{
if( (1<<(7-i))& g ) //如果是1
DmaTxBuffer = 0xff; //写入1码电平对应的数据
else //如果是0
DmaTxBuffer = 0xf0; //写入0码电平对应的数据
}
for(i=0;i<8;i++) //循环八次,先将红色色的数值写入缓存数组
{
if( (1<<(7-i))& r ) //如果是1
DmaTxBuffer = 0xff; //写入1码电平对应的数据
else //如果是0
DmaTxBuffer = 0xf0; //写入0码电平对应的数据
}
for(i=0;i<8;i++) //循环八次,先将蓝色的数值写入缓存数组
{
if( (1<<(7-i))& b ) //如果是1
DmaTxBuffer = 0xff; //写入1码电平对应的数据
else //如果是0
DmaTxBuffer = 0xf0; //写入0码电平对应的数据
}
}
然后控制颜色自动切换并刷新就好了void WS_task(void)
{
u8 i;
static u16 tim=0; //计时变量
if( tim<2048 ) //如果时间小于2048ms
{
for(i=0;i<12;i++) //循环12次
{
WS_Write(i+1,(u8)0,0,tim/8); //给12个灯都写入蓝色,蓝色的数值= 时间/8,数值逐渐变大
}
}
else if( tim<4096 ) //如果时间小于2048ms
{
for(i=0;i<12;i++) //循环12次
{
WS_Write(i+1,(u8)0,0,(4095-tim)/8); //给12个灯都写入蓝色,数值变小
}
}
tim++; //计时变量自加
if( tim>4096 ) //大于4096,自动清0
tim=0;
SPI_Write_Nbytes(24*12); //刷新颜色
}当然这里实现的就是一个最简单的蓝色呼吸效果(个人喜欢蓝色,所以就写了这个,当然也可以自己通过调用WS_Write函数写入自己喜欢的颜色,最后通过SPI_Write_Nbytes输出控制灯显示响应的颜色即可)
(4)蜂鸣器处理:
这里的蜂鸣器由于是无源的,所以要用PWM给他提供方波信号!void PWM_Init(void)
{
PWMB_CCER1 = 0x00; //写 CCMRx 前必须先清零 CCxE 关闭通道
PWMB_CCMR2 = 0x68; //配置PWM模式
PWMB_CCER1 = 0x30; //配置通道输出使能和极性
PWMB_ARRH = (u8)(PWM_PERIOD >> 8); //设置周期时间
PWMB_ARRL = (u8)PWM_PERIOD;
PWMB_ENO = 0x00;
PWMB_ENO |= ENO6P; //使能输出
PWMB_PS = 0x00; //高级 PWM 通道输出脚选择位
PWMB_PS |= PWM6_1; //选择 PWM7_1 通道
PWMB_BKR = 0x80; //使能主输出
PWMB_CR1 |= 0x81; //使能ARR预装载,开始计时
}代码比较简单,就不介绍了,看不懂的可以去看视频教程!void PWM_Set(u16 fre)
{
if( fre>0 ) //如果频率大于0
{
PWMB_ARRH = (u8)(fre >> 8); //设置周期时间
PWMB_ARRL = (u8)fre;
PWMB_CCR6H = (u8)((fre/2) >> 8); //设置占空比时间
PWMB_CCR6L = (u8)((fre/2));
PWMB_ENO |= ENO6P; //使能输出
}
else
PWMB_ENO = 0; //关闭输出
}
这里直接控制蜂鸣器开始和关闭
最终主函数的代码如下图所示!!void main()
{
WTST = 0; //设置程序指令延时参数,赋值为0可将CPU执行指令的速度设置为最快
EAXFR = 1; //扩展寄存器(XFR)访问使能
CKCON = 0; //提高访问XRAM速度
P5M0 = 0X10;P5M1 = 0X00; //设置为准双向口
P3M0 = 0X00;P3M1 = 0X00; //设置为准双向口
P2M0 = 0X00;P2M1 = 0X00; //设置为准双向口
P1M0 = 0X00;P1M1 = 0X00; //设置为准双向口
uart_init(); //串口初始化(这里备用)
usb_init(); //usb初始化,作为CDC+HID复合设备
Timer0_Init(); //定时器0初始化,提供ms时基
SPI_init(); //SPI初始化,这里驱动WS的灯
DMA_Config(); //DMA初始化
WS_Init(); //ws灯的驱动参数初始化
PWM_Init(); //驱动蜂鸣器的PWM初始化
PWM_Set(6000); //打开蜂鸣器
EA = 1; //使能总中断
Delay50ms(); //延时50ms,测试蜂鸣器
PWM_Set(0); //关闭蜂鸣器
while (1)
{
if (DeviceState != DEVSTATE_CONFIGURED) //等待USB连接完成
continue;
if( tim_1ms ) //1MS定时到达
{
tim_1ms=0;
CDC_task(); //串口修改数据任务
HID_task(); //HID发送键码任务
WS_task(); //灯光控制任务
}
}
}
源代码放在附件了,有需要的自行下载!有需要成品的也可以直接在这链接购买:https://item.taobao.com/item.htm ... ECf&id=742811211185
最后插一嘴:注意:代码里的VID和PID不能随意使用,仅供参考,商用的话请向STC官方申请或自行购买,不然出现任何问题本作者概不负责! 要看懂这个【USB摸鱼】的程序,可能还得来上课
=============================================
【USB 原理及实战,16课时】,视频教学已完美完成 ,大学标准课程 !
【10月/9号,10月/11号】USB基本原理教学视频, 已上传
【10月/16号的 USB-HID 通信 实战】教学视频超级完美, 已上传
是对着协议和代码一行一行的讲解,认真听的都说会了
【10月/18号下午的 USB-CDC虚拟串口 实战】教学视频, 已上传
USB-CDC虚拟串口 / 就是最简单最强大的串口
是对着协议和代码一行一行的讲解,认真听的都说会了
请帮忙转发给可能需要:从0开始了解 USB 的 同学/同事/老师/研发人员
https://www.stcaimcu.com/forum.php?mod=viewthread&tid=4526&extra=&page=1
=========================================
【CAN 原理及实战,8课时】,教学视频,制作中,后续直接看视频回放
{:4_250:} 本帖最后由 32位8051-STCAI 于 2023-10-11 11:58 编辑
原理图:
本帖最后由 电子DIY小家 于 2023-10-17 15:58 编辑
有小伙伴再问这个要怎么下载代码:
一、USB-CDC不停电下载(上一次下载的程序里需要包含一段不停电下载的代码才可以使用):
USB线连接之后,进行如下设置
上述设置完成之后,打开代码,点击下载等待完成即可
二、LINK1D下载:
LINK1D连接电脑和小键盘之后,按照如下设计即可:
如果下载窗口出现等待USB-HID,把这两个勾勾去掉就可以了,
冲哥开源作品继续玩,想怎么定义怎么定义。
attach://24436.mp4
网友问:这个 USB-HID键盘 扩展成支持 :
控制电脑:音量+ / 音量- 及【播放|暂停】功能
摸鱼键盘 自定义快捷键 HID免驱 STC主控 MCU代码开源 DIY键盘-淘宝网 (taobao.com)
51开源 USB 三键摸鱼 键盘@STC32G, 或者自己改成STC8H8K64U
单机V1.1版本预告:
把小键盘的单个HID通道的Consumer Control和Keyboard功能结合起来了,可以同时控制音量加减和标准键盘用了,驱动代码已调通,区别于隔壁某恒还是用的两个端点做的,这个单点方案的网上开源资料太少,这个版本的整理完直接在这里开源。
{:4_196:}冲哥威武
电子DIY小家 发表于 2023-11-1 16:01
单机V1.1版本预告:
把小键盘的单个HID通道的Consumer Control和Keyboard功能结合起来了,可以同时控制音 ...
内测版程序群文件自取哈
页:
[1]
2