- /**********************************************************************
- 文件名: 基于STM8S207的PAJ7620U2手势识别测试程序
- 编 写: Michael Lee
- 单 位:
- 时 间: 2020年10月20日
- 修 改:
- 备 注:
- 1. CPU主时钟选择内部16MHz RC振荡器;
- 2. 接口说明:
- STM8S207CBT6 ----- 外部设备
- ******** 12864液晶显示器 ********
- PD2 ----- YJ_RS
- PD3 ----- YJ_RW
- PD4 ----- YJ_E
- PC1 ----- YJ_PSB
- PB0-7 ----- YJ_DB0-7
- ******** STM8S硬件I2C ********
- PE1 ----- PAJ7620_SCL
- PE2 ----- PAJ7620_SDA
- 声明:
- **********************************************************************/
- //加载头文件
- #include "stm8s207cb.h"
- //宏定义
- #define uchar unsigned char
- #define uint unsigned int
- #define ushort unsigned short
- #define EEP_BASE 0x4000 //STM8S大容量Data EEPROM起始地址
- #define EEPROM_Lock() FLASH_IAPSR&=(~0x08)
- #define EEPROM_Wait_Read_Over() while(!(FLASH_IAPSR&0x04))
- #define EEPROM_Wrover_Clear() FLASH_IAPSR&=(~0x04)
- #define PAJ7620_ID 0x73<<1 //PAJ7620设备地址(写命令)
- //全局变量
- uchar Gesture_Flag=0; //手势识别结果标志
- //PAJ7620初始化数据及宏定义
- #define INIT_SIZE sizeof(Init_Array)/2
- //上电初始化寄存器数组
- const unsigned char Init_Array[][2]=
- {
- {0xEF,0x00},
- {0x37,0x07},
- {0x38,0x17},
- {0x39,0x06},
- {0x41,0x00},
- {0x42,0x00},
- {0x46,0x2D},
- {0x47,0x0F},
- {0x48,0x3C},
- {0x49,0x00},
- {0x4A,0x1E},
- {0x4C,0x20},
- {0x51,0x10},
- {0x5E,0x10},
- {0x60,0x27},
- {0x80,0x42},
- {0x81,0x44},
- {0x82,0x04},
- {0x8B,0x01},
- {0x90,0x06},
- {0x95,0x0A},
- {0x96,0x0C},
- {0x97,0x05},
- {0x9A,0x14},
- {0x9C,0x3F},
- {0xA5,0x19},
- {0xCC,0x19},
- {0xCD,0x0B},
- {0xCE,0x13},
- {0xCF,0x64},
- {0xD0,0x21},
- {0xEF,0x01},
- {0x02,0x0F},
- {0x03,0x10},
- {0x04,0x02},
- {0x25,0x01},
- {0x27,0x39},
- {0x28,0x7F},
- {0x29,0x08},
- {0x3E,0xFF},
- {0x5E,0x3D},
- {0x65,0x96},
- {0x67,0x97},
- {0x69,0xCD},
- {0x6A,0x01},
- {0x6D,0x2C},
- {0x6E,0x01},
- {0x72,0x01},
- {0x73,0x35},
- {0x74,0x00},
- {0x77,0x01},
- };
- #define GESTURE_SIZE sizeof(Gesture_Arry)/2
- //手势识别初始化寄存器数组
- const unsigned char Gesture_Arry[][2]=
- {
- {0xEF,0x00},
- {0x41,0x00},
- {0x42,0x00},
- {0xEF,0x00},
- {0x48,0x3C},
- {0x49,0x00},
- {0x51,0x10},
- {0x83,0x20},
- {0x9F,0xF9},
- {0xEF,0x01},
- {0x01,0x1E},
- {0x02,0x0F},
- {0x03,0x10},
- {0x04,0x02},
- {0x41,0x40},
- {0x43,0x30},
- {0x65,0x96},
- {0x66,0x00},
- {0x67,0x97},
- {0x68,0x01},
- {0x69,0xCD},
- {0x6A,0x01},
- {0x6B,0xB0},
- {0x6C,0x04},
- {0x6D,0x2C},
- {0x6E,0x01},
- {0x74,0x00},
- {0xEF,0x00},
- {0x41,0xFF},
- {0x42,0x01},
- };
- /********** 延时函数 ********/
- /***********************************************************
- 函数名: delay_us()、delay_ms()
- 功 能: 微秒级、毫秒级延时程序
- 参 数: us --- 欲延时微秒数
- ms --- 欲延时毫秒数
- /**********************************************************/
- void delay_us(unsigned int us)
- {
- //24M 1个int us延时0.68uS
- //16M 1.02us
- while(us--);
- }
- void delay_ms(unsigned int ms)//16MHZ 延时1ms
- {
- unsigned int i;
- while(ms--)
- {
- for(i=0;i<1421;i++){} //1421
- }
- }
- /******* 系统时钟切换操作 *******/
- /***********************************************************
- 函数名: Clk_Init()
- 功 能: 时钟初始化
- 参 数: 无
- 返回值: 无
- /**********************************************************/
- void Clk_Init(void)
- {
- CLK_CKDIVR = 0x00;
- //配置系统内部时钟分频系数为1(16M),CPU时钟分频系数为1(16M)
- }
- /********* 端口初始化操作 **********/
- /***********************************************************
- 函数名: Port_Init()
- 功 能: 端口初始化配置
- 参 数: 无
- 返回值: 无
- /**********************************************************/
- void Port_Init(void)
- {
- //12864液晶控制接口
- PC_DDR |= 0x04; //配置PC2为输出方式(液晶显示器背光控制端口)
- PC_CR1 |= 0x04; //配置端口推挽输出
- PC_CR2 = 0x00; //普通2M的输出摆率
- PC_ODR |= 0x04; //YJ_ON=1,选择开启液晶背光
- PD_DDR |= 0x1C; //配置PD2-4为输出方式(液晶显示器RS,RW,E端口)
- PD_CR1 |= 0x1C; //配置端口推挽输出
- PD_CR2 = 0x00; //普通2M的输出摆率
- PB_DDR |= 0xFF; //配置PB0-7输出(液晶数据DB0-7端口)
- PB_CR1 |= 0xFF; //推挽输出
- PB_CR2 = 0x00; //普通2M摆率
- }
- /******* 12864液晶(ST7920主控IC)显示器驱动及相关操作 *******/
- //底层驱动函数
- //写入一字节指令/数据
- /***********************************************************
- 函数名: lcd_w_char()
- 功 能: 向12864液晶写入一字节指令/数据
- 参 数: cd --- 写入指令/数据选择标志(0=指令,1=数据)
- cdata --- 写入指令/数据内容
- 返回值: 无
- /**********************************************************/
- void lcd_w_char(uchar cd, uchar cdata)
- {
- if(cd == 0) //选择写入"指令"
- {
- PD_ODR &= (~0x04); //RS=0,选择写"指令"
- }
- if(cd == 1) //选择写入"数据"
- {
- PD_ODR |= 0x04; //RS=1,选择写"数据"
- }
- PD_ODR &= (~0x08); //RW=0,选择"写"操作
- PB_ODR = cdata; //加载指令/数据内容
- PD_ODR |= 0x10; //E=1,开始写入指令/数据
- delay_us(1); //延时,等待液晶端口读入指令/数据内容
- PD_ODR &= (~0x10); //E=0,写入完成
- delay_us(80); //液晶处理指令时间一般为72us
- }
- //写入一串字符数据
- /***********************************************************
- 函数名: lcd_w_word()
- 功 能: 向12864液晶写入一串字符数据(最长64字符: 8个汉字或16个数字/英文字符)
- (注: ST7920主控IC的12864液晶内部已集成汉字库,程序可直接写入汉字字符)
- 参 数: str --- 字符串内容
- 返回值: 无
- ***********************************************************/
- void lcd_w_word(uchar *str)
- {
- while(*str != '\0') //字符串未结束
- {
- lcd_w_char(1,*str++); //写入每个字符串数据内容
- }
- *str = 0;
- }
- //初始化函数
- /***********************************************************
- 函数名: Lcd_Init()
- 功 能: 12864液晶初始化
- 参 数: 无
- 返回值: 无
- /**********************************************************/
- void Lcd_Init(void)
- {
- lcd_w_char(0,0x30); //写入指令,选择8位界面,基本指令集,绘图显示关
- lcd_w_char(0,0x0c); //显示开,光标关闭
- lcd_w_char(0,0x01); //清屏
- delay_ms(5); //液晶处理指令时间4.6ms
- }
- /********* STM8S硬件I2C相关操作 **********/
- //初始化函数
- /***********************************************************
- 函数名: I2C_Init()
- 功 能: STM8S207 MCU硬件I2C初始化
- (注: 主模式,两线连接,7位地址模式,40Kbps通信速率)
- 参 数: 无
- 返回值: 无
- ***********************************************************/
- void I2C_Init(void)
- {
- I2C_CR1 &= 0xFE; //PE=0,禁用硬件I2C模块(所有位置0)
- CLK_PCKENR1 |= 0x01; //使能I2C时钟
- I2C_CR2 |= 0x80; //I2C_CR2控制寄存器SWRST=1,软件复位硬件I2C模块
- I2C_CR2 &= 0x7F; //I2C_CR2控制寄存器SWRST=0,软件复位硬件I2C模块结束
- I2C_FREQR = 0x02; //配置I2C外设时钟2MHz
- I2C_CCRH = 0x00; //配置I2C为标准模式及速率
- I2C_CCRL = 0x32; //I2C通信速率半周期=((1/2)*50us=25us,速率=1/25us=40KHz
- I2C_TRISER = 0x03; //配置I2C上升时间寄存器,1000ns(SCL最大值)/500ns(2MHz周期)+1
- I2C_CR1 |= 0x01; //启动I2C硬件模块
- }
- /******** PAJ7620手势识别IC驱动及相关数据处理操作 *********/
- /***********************************************************
- 函数名: PAJ7620_Read()
- 功 能: STM8S207 MCU通过硬件I2C通信读取PAJ7620内多个寄存器数据
- 参 数: reg_address --- 欲读取数据的寄存器起始地址
- data_buff --- 从PAJ7620读取的多字节数据指针
- data_len --- 从PAJ7620读取的多字节数据长度
- 返回值: 无
- ***********************************************************/
- void PAJ7620_Read(uchar reg_address,uchar *data_buff,uchar data_len)
- {
- uchar temp; //定义一下,读取MCU状态寄存器用
- uchar i; //定义一下,供循环使用
- uint time; //定义一下,供等待延时用
- //以下程序为I2C通信防锁死操作,非常重要!
- if((PE_IDR&0x04) == 0x00) //检测总线是否真的忙碌中(忙碌则SCL=1,SDA=0)
- {
- I2C_CR1 &= 0xFE; //PE=0,禁用硬件I2C模块,准备直接操作MCU端口
- PE_DDR |= 0x02; //设置SCL端口为输出
- PE_CR1 |= 0x02; //上拉输出
- for(i=0;i<9;i++) //SCL端口发出9个时钟脉冲,让从设备DS3231释放总线(恢复SDA=1)
- {
- PE_ODR &= 0xFD; //SCL=0;
- delay_us(50); //延时(I2C通信频率10KHz)
- PE_ODR |= 0x02; //SCL=1
- delay_us(50); //延时(I2C通信频率10KHz)
- }
- time = 500; //变量赋值,准备延迟等待500次
- while(!(PE_IDR&0x04)) //等待数据线SDA=1
- {
- if(!--time) //变量自减
- return; //延迟等待500次后,未达到预期效果,退出子函数以防程序死机状态
- }
- PE_DDR &= 0xFD; //SCL端口恢复输入方式
- PE_CR1 &= 0xFD; //浮空输入
- PE_ODR &= 0xFD; //清零输出寄存器
- I2C_CR1 |= 0x01; //重新启动硬件I2C模块,接管SCL、SDA端口
- return; //退出函数,放弃本次数据读取操作
- }
- //以上程序为I2C通信防锁死操作,非常重要!
- //以下程序为STM8S硬件I2C通信防BUSY锁死操作,非常重要!
- if((I2C_SR3&0x02) == 0x02) //检测总线是否正确释放空闲中(总线正确释放则I2C_SR3状态寄存器BUSY=0,否则BUSY=1)
- {
- I2C_CR2 |= 0x80; //I2C_CR2控制寄存器SWRST=1,软件复位硬件I2C模块
- I2C_CR2 &= 0x7F; //I2C_CR2控制寄存器SWRST=0,软件复位硬件I2C模块结束
- I2C_FREQR = 0x02; //配置I2C外设时钟2MHz
- I2C_CCRH = 0x00; //配置I2C为标准模式及速率
- I2C_CCRL = 0x64; //I2C通信速率半周期=((1/2)*100us=50us,速率=1/100us=10KHz
- I2C_TRISER = 0x03; //配置I2C上升时间寄存器,1000ns(SCL最大值)/500ns(2MHz周期)+1
- I2C_CR1 |= 0x01; //启动I2C硬件模块
- return; //退出函数,放弃本次数据读取操作
- }
- //以上程序为STM8S硬件I2C通信防BUSY锁死操作,非常重要!
-
- /****** 第一步: 发送写指令(器件地址) ******/
- time = 500; //变量赋值,准备延迟等待500次
- while(I2C_SR3&0x02) //检测总线是否忙碌中(I2C_SR3状态寄存器BUSY位=1表示总线上有通信/忙碌)
- {
- if(!--time) //变量自减
- return; //延迟等待500次后,未达到预期效果,退出子函数以防程序死机状态
- }
- I2C_CR2 |= 0x01; //发起始条件(置位I2C_CR2控制寄存器START位)
- time = 500; //变量赋值,准备延迟等待500次
- while(!(I2C_SR1&0x01)) //I2C_SR1状态寄存器SB位=1表示发送成功
- {
- if(!--time) //变量自减
- return; //延迟等待500次后,未达到预期效果,退出子函数以防程序死机状态
- }
- delay_us(5); //短暂延时
- temp = I2C_SR1; //读取状态寄存器I2C_SR1值
- I2C_DR = PAJ7620_ID; //发送PAJ7620写指令(器件地址)
-
- /****** 第二步: 写入欲读取的寄存器首地址 ******/
- time = 500; //变量赋值,准备延迟等待500次
- while(!(I2C_SR1&0x02)) //I2C_SR1状态寄存器ADDR位=1时表示地址发送结束(主模式)
- {
- if(!--time) //变量自减
- return; //延迟等待500次后,未达到预期效果,退出子函数以防程序死机状态
- }
- delay_us(5); //短暂延时
- temp = I2C_SR1; //读取状态寄存器I2C_SR1数值
- temp = I2C_SR3; //清除状态寄存器I2C_SR1中ADDR标志位(地址已发送结束)
- I2C_DR = reg_address; //发送欲读取数据的寄存器首地址
-
- /****** 第三步: 发送读指令(器件地址|0x01) ******/
- time = 500; //变量赋值,准备延迟等待500次
- while(!(I2C_SR1&0x84)) //等待数据发送完毕(I2C_SR1状态寄存器TxE、BTF位为1时,表示发送时数据寄存器为空且数据字节发送完毕)
- {
- if(!--time) //变量自减
- return; //延迟等待500次后,未达到预期效果,退出子函数以防程序死机状态
- }
- I2C_CR2 |= 0x01; //重复发起始条件(置位I2C_CR2控制寄存器START位)
- time = 500; //变量赋值,准备延迟等待500次
- while(!(I2C_SR1&0x01)) //I2C_SR1状态寄存器SB位=1表示发送成功
- {
- if(!--time) //变量自减
- return; //延迟等待500次后,未达到预期效果,退出子函数以防程序死机状态
- }
- delay_us(5); //短暂延时
- temp = I2C_SR1; //读取状态寄存器I2C_SR1值
- I2C_DR = PAJ7620_ID|0x01; //发送PAJ7620读指令
-
- /****** 第四步: 读取数据 ******/
- time = 500; //变量赋值,准备延迟等待500次
- while(!(I2C_SR1&0x02)) //I2C_SR1状态寄存器ADDR位=1时表示地址发送结束(主模式)
- {
- if(!--time) //变量自减
- return; //延迟等待500次后,未达到预期效果,退出子函数以防程序死机状态
- }
- delay_us(5); //短暂延时
- temp = I2C_SR1; //读取状态寄存器I2C_SR1数值
- temp = I2C_SR3; //清除状态寄存器I2C_SR1中ADDR标志位(地址已发送结束)
- I2C_CR2 |= 0x04; //使能ACK应答(置位I2C_CR2控制寄存器ACK位,收到一个字节数据或地址后返回应答)
- while(data_len) //循环读取数据
- {
- if(data_len == 1) //如果是接收最后一位数据(特殊处理)
- {
- I2C_CR2 &= 0xFB; //关闭ACK应答(清零I2C_CR2控制寄存器ACK位,收到一个字节数据或地址后不返回应答)
- I2C_CR2 |= 0x02; //发停止条件(置位I2C_CR2控制寄存器STOP位///必须先清除I2C_SR1状态寄存器BTF位)
- }
- if(I2C_SR1 & 0x40) //数据寄存器为满(I2C_SR1状态寄存器RxNE位为1时,表示接收寄存器数据非空)
- {
- *data_buff = I2C_DR; //读取数据寄存器内数值,同时清除BTF位
- data_buff++; //准备接收下一字节数据
- data_len--;
- }
- }
-
- /****** 第五步: 收尾 ******/
- temp = I2C_SR1; //读取状态寄存器I2C_SR1值
- temp = I2C_DR; //清除I2C_SR1中BFT位
- }