/
- @file i2c_keyboard.c
- @brief 4x4 矩阵键盘 I2C 驱动实现文件
- @author Based on CSDN Blog: https://blog.csdn.net/m0_/article/details/ */
#include "i2c_keyboard.h" #include
/
- @brief I2C 延时函数
- @note 软件 I2C 需要适当的延时来保证时序正确 */ void I2C_Delay(void) { HAL_Delay(4); }
/
- @brief I2C 初始化函数
- @note 配置 PB6 和 PB7 为开漏输出模式 */ void I2C_Init(void) { // 启用 GPIOB 时钟 __HAL_RCC_GPIOB_CLK_ENABLE();
// 定义 GPIO 初始化结构体 GPIO_InitTypeDef GPIO_InitStructure = {0};
// 配置 I2C 引脚为开漏输出模式 GPIO_InitStructure.Pin = I2C_Pin_SCL | I2C_Pin_SDA; GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD; // 开漏输出模式 GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_HIGH; // 高速模式 GPIO_InitStructure.Pull = GPIO_NOPULL;
HAL_GPIO_Init(I2C_Port, &GPIO_InitStructure);
// 释放 SCL 和 SDA 总线(上拉) HAL_GPIO_WritePin(I2C_Port, I2C_Pin_SCL, GPIO_PIN_SET); HAL_GPIO_WritePin(I2C_Port, I2C_Pin_SDA, GPIO_PIN_SET); }
/
- @brief I2C 开始信号
- @note SCL 高电平时,SDA 由高变低 */ void I2C_Start(void) { I2C_W_SDA(1); I2C_W_SCL(1); I2C_Delay(); I2C_W_SDA(0); I2C_Delay(); I2C_W_SCL(0); I2C_Delay(); }
/
- @brief I2C 停止信号
- @note SCL 高电平时,SDA 由低变高 */ void I2C_Stop(void) { I2C_W_SDA(0); I2C_Delay(); I2C_W_SCL(1); I2C_Delay(); I2C_W_SDA(1); I2C_Delay(); }
/
- @brief I2C 发送一个字节
- @param address 要发送的字节数据
- @note 高位先发,在 SCL 低电平期间改变数据线状态 */ void I2C_SendByte(uint8_t data)
else { I2C_W_SDA(0); } data <<= 1; I2C_Delay(); I2C_W_SCL(1); I2C_Delay(); I2C_Delay();}
I2C_W_SCL(0); // 确保最后 SCL 为低电平 }
/
- @brief I2C 主机发送 ACK 应答信号
- @note 用于读取数据时通知从机继续发送 */ void I2C_ACK(void) { I2C_W_SCL(0); I2C_W_SDA(0); I2C_Delay(); I2C_W_SCL(1); I2C_Delay(); I2C_W_SCL(0); I2C_Delay(); }
/
- @brief I2C 主机发送 NACK 应答信号
- @note 用于读取最后一个字节时通知从机停止发送 */ void I2C_NACK(void) { I2C_W_SCL(0); I2C_W_SDA(1); I2C_Delay(); I2C_W_SCL(1); I2C_Delay(); I2C_W_SCL(0); I2C_Delay(); }
/
- @brief I2C 读取一个字节
- @param flag 1=发送 ACK,0 发送 NACK
- @return 读取到的字节数据
- @note 主机释放 SDA 总线,从机控制数据线 */ uint8_t I2C_ReadByte(uint8_t flag)
I2C_Delay();}
I2C_W_SCL(0);
// 根据 flag 发送 ACK 或 NACK if(flag) {
I2C_ACK();} else {
I2C_NACK();}
return byte; }
/
- @brief 获取按键值
- @param value 指向存储按键值的指针
- @note 从 VK36N16I 芯片读取两个字节并合并为 uint16_t
- 特别注意:每个 SDA 字节读取之前必须进行 ACK 应答,否则读取不到数据,共应答两次 */ void Get_KeyBoard_Value(uint16_t *value) { uint8_t byte_low = 0; uint8_t byte_high = 0;
// 开始信号 I2C_Start();
// 发送读地址 (0xCB) I2C_SendByte(KEYBOARD_I2C_ADDR_READ);
// 第一个字节读取前进行 ACK 应答 I2C_ACK();
// 读低字节 byte_low = I2C_ReadByte(1); // 发送 ACK,准备读下一个字节
// 第二个字节读取前进行 ACK 应答 I2C_ACK();
// 读高字节 byte_high = I2C_ReadByte(0); // 发送 NACK,最后一个字节
// 停止信号 I2C_Stop();
// 将低字节和高字节合并为一个 uint16_t *value = ((uint16_t)byte_high << 8) | byte_low; }
/
- @brief 根据按键值映射返回对应的字符
- @param key_value 按键的原始值
- @return 对应的字符,未匹配返回’!’
- 按键值映射表 (十进制):
- 1 -> ‘1’, 16 -> ‘2’, 256 -> ‘3’, 2 -> ‘4’
- 32 -> ‘5’, 512 -> ‘6’, 4 -> ‘7’, 64 -> ‘8’
- 1024 -> ‘9’, 128 -> ‘0’, 8 -> ‘*’, 2048 -> ‘#’
- 4096 -> ‘A’, 8192 -> ‘B’, 16384 -> ‘C’, 32768 -> ’D’ / char get_key_from_value(int key_value) { switch(key_value) { case 1: return ‘1’; case 16: return ‘2’; case 256: return ‘3’; case 2: return ‘4’; case 32: return ‘5’; case 512: return ‘6’; case 4: return ‘7’; case 64: return ‘8’; case 1024: return ‘9’; case 128: return ‘0’; case 8: return ‘’; case 2048: return ‘#’; case 4096: return ‘A’; case 8192: return ‘B’; case 16384: return ‘C’; case 32768: return ’D’; default: return ‘!’; // 如果没有匹配的值,返回特定字符 } }
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/256315.html