大家好,在上一期的帖子中我们已经成功通过threadx线程把数据通过串口打印出来了
这一期主要两个事情
1.成功驱动IIC手势识别模块
2.实现usb(hid keyboard)功能
注意:这里给大家一个建议,很重要
代码不要一次性全部完成再调试,写一点就运行看看板子功能是否正常。
本人调试USB时就全部写完了,后面发现板子运行异常,最后是改一些就运行观察现象
最后才把USB调试通过
话不多说,进入正题
我们先来调试IIC手势传感器
先来看一下IIC接口的简介
I²C只使用两条双向漏极开路(Open Drain)线,其中一条线为传输数据的串行资料线(SDA, Serial DAta line),另一条线是启动或停止传输以及发送时钟序列的串行主频(SCL, Serial CLock line)线,这两条线上都有上拉电阻[2]。I²C允许相当大的工作电压范围,但典型的电压准位为+3.3V或+5v。
I²C的参考设计使用一个7比特长度的地址空间但保留了16个地址,所以在一组总线最多可和112个节点通信[a]。常见的I²C总线依传输速率的不同而有不同的模式:标准模式(100 kbit/s)、低速模式(10 kbit/s),但时钟频率可被允许下降至零,这代表可以暂停通信。而新一代的I²C总线可以和更多的节点(支持10比特长度的地址空间)以更快的速率通信:快速模式(400 kbit/s)、快速+模式(1 Mbit/s)高速模式(3.4 Mbit/s)超高速模式(5 Mbit/s)。
I²C被应用在构造简单且可以牺牲传输速度来降低制造成本的外设上。一些常见的应用如下:
为了保存用户的设置而访问NVRAM芯片。
访问低速的数字模拟转换器(DAC)和模拟数字转换器(ADC)。
改变监控器的对比度、色调及色彩平衡设置(VESA显示数据频道)。
控制小型液晶或OLED屏幕。
改变音量大小。
获取硬件监控及诊断资料,例如中央处理器的温度及风扇转速。
读取实时时钟(Real-time clock)。
在系统设备中用来开启或关闭电源供应。
下面是手势传感器的相关信息
硬件接线见下图
配置硬件IIC
相关驱动函数添加
驱动函数来自坛友共享代码,在这里感谢坛友
接下来
我们先添加printf函数方便调试信息打印。
https://shequ.stmicroelectronics.cn/thread-639670-1-1.html
相关printf教程可以参考上面的链接
在main.c中添加代码
/* USER CODE BEGIN Includes */ #include "stdio.h" /* USER CODE END Includes */ /* USER CODE BEGIN 0 */ #ifdef __GNUC__ #define PUTCHAR_PROTOTYPE int __io_putchar(int ch) #else #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f) #endif PUTCHAR_PROTOTYPE { HAL_UART_Transmit(&huart3 , (uint8_t *)&ch, 1, 0xFFFF); return ch; } /* USER CODE END 0 */
这样就能在工程其他地方愉快使用printf函数了,话说这里我搞了一小时。
论坛上坛友的教程只针对keil有效,这个例子基本对大部分IDE是通用的。
本人使用的cubemxIDE.
下面在app_threadx.c中添加手势传感器测试代码
/* USER CODE BEGIN Includes */ #include "main.h" #include "i2c.h" #include "icache.h" #include "memorymap.h" #include "usart.h" #include "gpio.h" #include "stdio.h" #include <string.h> #include "stdlib.h" #include "Gesture.h" #include "paj7620.h" /* USER CODE END Includes */ /* USER CODE BEGIN 1 */ void my_thread_enytry(ULONG initial_input) { printf("PAJ7620U2 Gesture Sensor Test Program ...\r\n"); PAJ7620_Init(); printf("PAJ7620U2 Gesture Sensor OK \r\n"); while(1){ HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); printf("my thread test\r\n"); GetCurrentGesture(); tx_thread_sleep(50); } } /* USER CODE END 1 */
这里是打印信息
[14:37:31.262]收←◆PAJ7620U2 Gesture Sensor Test Program ... [14:37:31.857]收←◆PAJ7620U2 Gesture Sensor Test Program ... [14:37:33.218]收←◆PAJ7620U2 OK PAJ7620U2 Gesture Sensor OK EEPW user:rasngsun [14:37:38.716]收←◆Right [14:37:40.715]收←◆Left [14:37:42.216]收←◆Up [14:37:43.716]收←◆Down [14:37:46.216]收←◆Clockwise [14:37:46.716]收←◆Down [14:37:48.716]收←◆Down [14:37:51.216]收←◆Forward [14:37:51.716]收←◆Backward [14:37:53.216]收←◆Forward [14:37:53.716]收←◆Backward
至此我们就可以调试USB Hid keyboard了
这里说一下主要步骤
第一步初始化USB外设,配置USB描述信息,启动USB外设
第二步就是需要创建一个USB线程去周期性读取IIC手势传感器信息通过键盘发送出去
老师视频里很多细节没有讲清楚,有可能是想让我等学院多动脑思考,好在没有辜负老师的一片苦心。
USB部分的功能其实在写这篇文章的前天就调试通过了,这里主要是巩固加强一下。
话不多说,首先配置打开USB外设,配置device模式,开启中断
接下来配置usbx组件
编辑app_usb_device.c,添加如下代码
/* USER CODE BEGIN Includes */ c /* USER CODE END Includes */ /* USER CODE BEGIN PV */ static TX_THREAD ux_hid_thread; extern PCD_HandleTypeDef hpcd_USB_DRD_FS; /* USER CODE END PV */ /* USER CODE BEGIN PFP */ VOID USBX_APP_Device_Init(VOID); /* USER CODE END PFP */ /* USER CODE BEGIN 1 */ VOID USBX_APP_Device_Init(VOID) { /* 初始化外设,必不可少 */ MX_USB_PCD_Init(); /* 外设配置 */ HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, 0x00, PCD_SNG_BUF, 0X18); HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, 0x80, PCD_SNG_BUF, 0X58); HAL_PCDEx_PMAConfig(&hpcd_USB_DRD_FS, USBD_HID_KEYBOARD_EPIN_ADDR, PCD_SNG_BUF, 0X98); ux_dcd_stm32_initialize((ULONG)USB_DRD_FS, (ULONG)&hpcd_USB_DRD_FS); /* 打开外设 */ HAL_PCD_Start(&hpcd_USB_DRD_FS); } /* USER CODE END 1 */
接下来修改static VOID app_ux_device_thread_entry(ULONG thread_input)线程函数
在线程中初始化USB外设
/** * @brief Function implementing app_ux_device_thread_entry. * @param thread_input: User thread input parameter. * @retval none */ static VOID app_ux_device_thread_entry(ULONG thread_input) { /* USER CODE BEGIN app_ux_device_thread_entry */ TX_PARAMETER_NOT_USED(thread_input); USBX_APP_Device_Init(); /* USER CODE END app_ux_device_thread_entry */ }
编译运行,观察效果
效果如何呢,不太理想,并没有识别到,需要调整USBX组件配置
UX_SLAVE_REQUEST_DATA_MAX_LENGTH = 64 /* 这个值太大会占用栈空间,默认最小值就行 */
UXDevice memory pool size = 8192 /* 扩大内存池空间 */
USBX Device System Stack Size = 4 * 1024 /* 扩大栈空间 */
调整参数后,重新编译运行
设备管理器应该能正常识别键盘了
下一步就是添加键值生成相关函数代码
修改ux_device_keyboard.c
/* USER CODE BEGIN Includes */ #include "main.h" /* USER CODE END Includes */ /* USER CODE BEGIN PV */ UX_SLAVE_CLASS_HID *hid_keyboard; /* USER CODE END PV */ /* USER CODE BEGIN 0 */ uint8_t keyboard[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; static void GetKeyboardData(UX_SLAVE_CLASS_HID_EVENT *hid_event) { keyboard[2] = 0x04; hid_event->ux_device_class_hid_event_length = 8; for (size_t i = 0; i < 8; i++) { hid_event->ux_device_class_hid_event_buffer[i] = keyboard[i]; } } static void ClearKeyboardData(UX_SLAVE_CLASS_HID_EVENT *hid_event) { hid_event->ux_device_class_hid_event_length = 8; for (size_t i = 0; i < 8; i++) { hid_event->ux_device_class_hid_event_buffer[i] = 0; } } /* USER CODE END 0 */
以上是添加的,还有两处需要修改
VOID USBD_HID_Keyboard_Activate(VOID *hid_instance) { /* USER CODE BEGIN USBD_HID_Keyboard_Activate */ // UX_PARAMETER_NOT_USED(hid_instance); hid_keyboard = (UX_SLAVE_CLASS_HID *)hid_instance; /* USER CODE END USBD_HID_Keyboard_Activate */ return; } VOID USBD_HID_Keyboard_Deactivate(VOID *hid_instance) { /* USER CODE BEGIN USBD_HID_Keyboard_Deactivate */ UX_PARAMETER_NOT_USED(hid_instance); hid_keyboard = UX_NULL; /* USER CODE END USBD_HID_Keyboard_Deactivate */ return; }
这两个代码是USB插拔的时候执行的相关功能代码。
接下来修改ux_device_keyboard.h添加外部声明
/* USER CODE BEGIN ET */ extern UX_SLAVE_CLASS_HID *hid_keyboard; extern static void GetKeyboardData(UX_SLAVE_CLASS_HID_EVENT *hid_event); extern static void ClearKeyboardData(UX_SLAVE_CLASS_HID_EVENT *hid_event); /* USER CODE END ET */
接下来我们需要调整IIC外设初始化函数的位置
修改app_threadx.c文件my_thread_enytry函数
把手势识别相关代码屏蔽
void my_thread_enytry(ULONG initial_input) { // printf("PAJ7620U2 Gesture Sensor Test Program ...\r\n"); //// PAJ7620_Init(); // printf("PAJ7620U2 Gesture Sensor OK \r\n"); // printf("EEPW user:rasngsun \r\n"); while(1){ HAL_GPIO_TogglePin(LED_GPIO_Port, LED_Pin); // GetCurrentGesture(); tx_thread_sleep(50); } }
手势识别模块读取将在app_usbx_device.c文件中完成
接下来编辑app_usbx_device.c文件
/* USER CODE BEGIN Includes */ #include "main.h" #include "i2c.h" #include "icache.h" #include "memorymap.h" #include "usart.h" #include "gpio.h" #include "stdio.h" #include <string.h> #include "stdlib.h" #include "Gesture.h" #include "paj7620.h" /* USER CODE END Includes */ static VOID app_ux_device_thread_entry(ULONG thread_input) { /* USER CODE BEGIN app_ux_device_thread_entry */ TX_PARAMETER_NOT_USED(thread_input); USBX_APP_Device_Init(); UX_SLAVE_DEVICE *device; UX_SLAVE_CLASS_HID_EVENT hid_event; device = &_ux_system_slave->ux_system_slave_device; ux_utility_memory_set(&hid_event, 0, sizeof(UX_SLAVE_CLASS_HID_EVENT)); PAJ7620_Init(); printf("app_ux_device_thread EEPW user:rasngsun \r\n"); while (1) { /* Check if the device state already configured */ if ((device->ux_slave_device_state == UX_DEVICE_CONFIGURED) && (hid_keyboard != UX_NULL)) { /* sleep for 10ms */ tx_thread_sleep(MS_TO_TICK(100)); hid_event.ux_device_class_hid_event_length = 8; hid_event.ux_device_class_hid_event_buffer[0] = 0; hid_event.ux_device_class_hid_event_buffer[1] = 0; int Gesture_Data = GetCurrentGesture(); /* 默认键值 */ UCHAR keyValue = 0; switch (Gesture_Data) { case PAJ_UP: //Keyboard PageUp keyValue = 0x4B; break; case PAJ_DOWN: //Keyboard PageDown keyValue = 0x4E; break; case PAJ_LEFT: //Keyboard LeftArrow keyValue = 0x50; break; case PAJ_RIGHT: //Keyboard RightArrow keyValue = 0x4F; break; case PAJ_FORWARD: //Keyboard Home keyValue = 0x4A; break; case PAJ_BACKWARD://Keyboard End keyValue = 0x4A; break; case PAJ_CLOCKWISE: break; case PAJ_COUNT_CLOCKWISE: break; case PAJ_WAVE: break; default: break; } /* 按键赋值 */ hid_event.ux_device_class_hid_event_buffer[2] = keyValue; /* 按键事件发送*/ ux_device_class_hid_event_set(hid_keyboard, &hid_event); } else { /* sleep for 10ms */ tx_thread_sleep(MS_TO_TICK(10)); } } /* USER CODE END app_ux_device_thread_entry */ }
我这里的代码直接在它现有的线程里面运行的,和老师讲的不太一样
没有单独创建usb_hid_keyboard_thread,这里还请留意。
条条大路通罗马,没必要执着。
能调试通过就可以。
下面来看效果:
我是通过网页在线keyboard event log工具查看的键盘事件
至此工作完成。
知其然当知其所以然。
b站链接
https://www.bilibili.com/video/BV16rageqEQT/