打印
[STM8]

STM8硬件I2C与PAJ7620U2手势识别芯片调试分享

[复制链接]
2056|28
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
这两天因为工作需要,用STM8单片机调试了一下PAJ7620U2手势识别芯片,网上几乎所有的资料都是STM32的,STM8或者8位单片机控制PAJ7620U2的例子不多,几乎没有,虽然STM32应用广泛,但实际上我们这些做小电子设备产品的暂时还用不上。调试了四五天,中途有个坑,被坑的很惨,大家有需要的可以参考一下,免得掉坑里。

使用特权

评论回复
沙发
henangongda123|  楼主 | 2020-10-26 14:47 | 只看该作者
硬件电路很简单,PAJ7620U2我是某宝上买的现成的模块,基本上都大同小异,就是2个LEDO和上拉电阻,因为PAJ7620U2的发射二极管功率较大,所以需要单独供电。电路参考如下:

PAJ7620U2模块.png (67.47 KB )

PAJ7620U2模块.png

使用特权

评论回复
板凳
henangongda123|  楼主 | 2020-10-26 14:50 | 只看该作者
然后就是PAJ7620U2的I2C接口和STM8S207的硬件I2C接口对接即可:

STM8S207硬件I2C接口.png (34.16 KB )

STM8S207硬件I2C接口.png

使用特权

评论回复
地板
henangongda123|  楼主 | 2020-10-26 14:54 | 只看该作者
最后说坑!这里面有2个坑,一个STM8S硬件I2C,这个坑在我之前做电子台历的时候,调试DS3231SN芯片已解决,大家有兴趣的可以去看一下我之前的帖子。这个坑的程序代码如下:
/******** 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位
}

一定要注意上面的I2C通信防锁死操作和防BUSY锁死操作,非常重要!

使用特权

评论回复
5
henangongda123|  楼主 | 2020-10-26 14:56 | 只看该作者
最后,还有一个坑就是PAJ7620U2连续写操作之间要加延时,一定要延时,10ms左右,不能太短,否则初始化数组写不成功的,这个坑搞了我好几天!代码如下:
/***********************************************************
函数名: PAJ7620_Init()
功  能: PAJ7620U2手势识别芯片初始化操作
参  数: 无
返回值: 无
***********************************************************/
void PAJ7620_Init(void)
{
  uchar i,PAJ7620_R1_Data1[1]={0x00}; //定义保存子函数临时使用数据
        do                          //执行唤醒操作
        {
          PAJ7620_WakeUp();         //唤醒PAJ7620
          delay_ms(5);              //延时等待(最低700us)
          PAJ7620_WakeUp();         //再次唤醒PAJ7620
          delay_ms(5);              //延时等待(最低700us)
          PAJ7620_SelectBank(0);    //选择BANK0寄存器区域
    PAJ7620_Read(0x00,PAJ7620_R1_Data1,1);//读取0x00寄存器数值,判断是否唤醒正确
        }
        while(PAJ7620_R1_Data1[0]==0x20);//直到正确唤醒
        PAJ7620_SelectBank(0);      //选择BANK0寄存器区域
        for(i=0;i<INIT_SIZE;i++)    //准备写入上电初始化初始化寄存器数据
        {
                PAJ7620_Write(Init_Array[i][0],Init_Array[i][1]); //上电初始化PAJ7620U2寄存器配置
                delay_ms(10);            //延时(此步非常重要,PAJ7620连续写操作需要间隔一段时间,否则不能正常写完)
        }
        PAJ7620_SelectBank(0);      //选择Bank0区域寄存器
        for(i=0;i<GESTURE_SIZE;i++) //准备写入手势模式初始化寄存器数据
        {
                PAJ7620_Write(Gesture_Arry[i][0],Gesture_Arry[i][1]); //初始化PAJ7620U2手势模式
                delay_ms(10);            //延时(此步非常重要,PAJ7620连续写操作需要间隔一段时间,否则不能正常写完)
        }
}

使用特权

评论回复
6
goyhuan| | 2020-10-26 15:53 | 只看该作者
谢谢分享

使用特权

评论回复
7
氧化二氢| | 2020-10-26 22:54 | 只看该作者
谢谢分享 请问你用的是8位的哪个板子

使用特权

评论回复
8
henangongda123|  楼主 | 2020-10-27 11:18 | 只看该作者
氧化二氢 发表于 2020-10-26 22:54
谢谢分享 请问你用的是8位的哪个板子

STM8S207CB主控板

使用特权

评论回复
9
奔牛滚滚| | 2020-10-27 13:39 | 只看该作者
感觉用硬件的模块比用io模拟的还复杂

使用特权

评论回复
10
henangongda123|  楼主 | 2020-10-27 17:04 | 只看该作者
奔牛滚滚 发表于 2020-10-27 13:39
感觉用硬件的模块比用io模拟的还复杂

貌似是,IO模拟的多

使用特权

评论回复
11
hjl2832| | 2020-10-27 20:49 | 只看该作者
这个好,谢谢分享,手势识别芯片编程。

使用特权

评论回复
12
henangongda123|  楼主 | 2020-10-28 15:49 | 只看该作者
小喇叭关注了,那就贴上完整代码吧!

使用特权

评论回复
13
henangongda123|  楼主 | 2020-10-28 15:53 | 只看该作者
/**********************************************************************
文件名: 基于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位
}

使用特权

评论回复
14
henangongda123|  楼主 | 2020-10-28 15:54 | 只看该作者
/***********************************************************
函数名: PAJ7620_Write()
功  能: STM8S207 MCU通过硬件I2C通信写入PAJ7620寄存器一字节数据
参  数: write_address --- 欲写入的寄存器地址
                                write_data    --- 欲写入的一字节数据
返回值: 无
***********************************************************/
void PAJ7620_Write(uchar write_address,uchar write_data)
{         
        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锁死操作,非常重要!
       
        /****** 第一步: 发送写指令(器件地址) ******/
        I2C_CR2 &= 0xFB;           //关闭ACK应答(清零I2C_CR2控制寄存器ACK位,收到一个字节数据或地址后不返回应答)
        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 = write_address;       //发送欲写入数据的寄存器首地址
       
        /****** 第三步: 写入一字节数据 ******/
        time = 500;                 //变量赋值,准备延迟等待500次
        while(!(I2C_SR1&0x84))      //等待数据发送完毕(I2C_SR1状态寄存器TxE、BTF位为1时,表示发送时数据寄存器为空且数据字节发送完毕)
        {
                if(!--time)               //变量自减
                return;                   //延迟等待500次后,未达到预期效果,退出子函数以防程序死机状态
        }
        I2C_DR = write_data;        //发送一字节数据
        while(!(I2C_SR1&0x84));   //等待数据发送完毕(I2C_SR1状态寄存器TxE、BTF位为1时,表示发送时数据寄存器为空且数据字节发送完毕)
       
        /****** 第四步: 收尾 ******/
        I2C_CR2 |= 0x02;            //发停止条件(置位I2C_CR2控制寄存器STOP位///必须先清除I2C_SR1状态寄存器BTF位)
}

/***********************************************************
函数名: PAJ7620_SelectBank
功  能: STM8S207 MCU通过硬件I2C通信选择操作寄存器区域
参  数: 无
返回值: 无
***********************************************************/
void PAJ7620_SelectBank(uchar bank)
{
        switch(bank)
        {
                case 0: PAJ7620_Write(0xEF,0x00);//选择BANK0寄存器区域
                                                break;
                case 1: PAJ7620_Write(0xEF,0x01);//选择BANK1寄存器区域
                                                break;
                default: break;
        }
}

/***********************************************************
函数名: PAJ7620_WakeUp()
功  能: STM8S207 MCU通过硬件I2C通信唤醒PAJ7620
参  数: 无
返回值: 无
***********************************************************/
void PAJ7620_WakeUp(void)
{
        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个时钟脉冲,让从设备PAJ7620释放总线(恢复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锁死操作,非常重要!
       
        /****** 第一步: 发送写指令(器件地址) ******/
        I2C_CR2 &= 0xFB;           //关闭ACK应答(清零I2C_CR2控制寄存器ACK位,收到一个字节数据或地址后不返回应答)
        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写指令(器件地址)
       
        /****** 第二步: 等待发送完毕 ******/
        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 |= 0x02;            //发停止条件(置位I2C_CR2控制寄存器STOP位///必须先清除I2C_SR1状态寄存器BTF位)
}

/***********************************************************
函数名: PAJ7620_Init()
功  能: PAJ7620U2手势识别芯片初始化操作
参  数: 无
返回值: 无
***********************************************************/
void PAJ7620_Init(void)
{
  uchar i,PAJ7620_R1_Data1[1]={0x00}; //定义保存子函数临时使用数据
        do                          //执行唤醒操作
        {
          PAJ7620_WakeUp();         //唤醒PAJ7620
          delay_ms(5);              //延时等待(最低700us)
          PAJ7620_WakeUp();         //再次唤醒PAJ7620
          delay_ms(5);              //延时等待(最低700us)
          PAJ7620_SelectBank(0);    //选择BANK0寄存器区域
    PAJ7620_Read(0x00,PAJ7620_R1_Data1,1);//读取0x00寄存器数值,判断是否唤醒正确
        }
        while(PAJ7620_R1_Data1[0]==0x20);//直到正确唤醒
        PAJ7620_SelectBank(0);      //选择BANK0寄存器区域
        for(i=0;i<INIT_SIZE;i++)    //准备写入上电初始化初始化寄存器数据
        {
                PAJ7620_Write(Init_Array[i][0],Init_Array[i][1]); //上电初始化PAJ7620U2寄存器配置
                delay_ms(10);            //延时(此步非常重要,PAJ7620连续写操作需要间隔一段时间,否则不能正常写完)
        }
        PAJ7620_SelectBank(0);      //选择Bank0区域寄存器
        for(i=0;i<GESTURE_SIZE;i++) //准备写入手势模式初始化寄存器数据
        {
                PAJ7620_Write(Gesture_Arry[i][0],Gesture_Arry[i][1]); //初始化PAJ7620U2手势模式
                delay_ms(10);            //延时(此步非常重要,PAJ7620连续写操作需要间隔一段时间,否则不能正常写完)
        }
}

/***********************************************************
函数名: Gesture_Get()
功  能: 获取当前手势识别结果
参  数: 无
返回值: 无
***********************************************************/
void Gesture_Get(void)
{
        uchar PAJ7620_R2_Data1[2]={0x00,0x00};//定义保存临时数据
        PAJ7620_SelectBank(0);      //选择Bank0区域寄存器
        PAJ7620_Read(0x43,PAJ7620_R2_Data1,2); //读取PAJ7620手势结果(Bank0区域0x43、0x44中断标志寄存器)
  if((PAJ7620_R2_Data1[0]&0x01)==0x01)
        {
                Gesture_Flag=1;           //Up
        }
        else if((PAJ7620_R2_Data1[0]&0x02)==0x02)
        {
                Gesture_Flag=2;           //Down
        }
        else if((PAJ7620_R2_Data1[0]&0x04)==0x04)
        {
                Gesture_Flag=3;           //Left
        }
        else if((PAJ7620_R2_Data1[0]&0x08)==0x08)
        {
                Gesture_Flag=4;           //Right
        }
        else if((PAJ7620_R2_Data1[0]&0x10)==0x10)
        {
                Gesture_Flag=5;           //Forward
        }
        else if((PAJ7620_R2_Data1[0]&0x20)==0x20)
        {
                Gesture_Flag=6;           //Backward
        }
        else if((PAJ7620_R2_Data1[0]&0x40)==0x40)
        {
                Gesture_Flag=7;           //Clockwise
        }
        else if((PAJ7620_R2_Data1[0]&0x80)==0x80)
        {
                Gesture_Flag=8;           //Counter Clockwise
        }
        else if((PAJ7620_R2_Data1[1]&0x01)==0x01)
        {
                Gesture_Flag=9;           //Wave
        }
}

/***********************************************************
函数名: Display()
功  能: 12864液晶显示屏主屏显示手势测试结果
参  数: 无
返回值: 无
***********************************************************/
void Display(void)
{
        lcd_w_char(0,0x80);         //第一行显示起始地址
        lcd_w_word("  手势识别测试  ");//"手势识别测试"
        lcd_w_char(0,0x90);         //第二行显示起始地址
        lcd_w_word("测试结果:      ");//"测试结果:"
        lcd_w_char(0,0x88);         //第一行显示起始地址
        switch(Gesture_Flag)
        {
                case 1: lcd_w_word("      向上      ");break; //向上
                case 2: lcd_w_word("      向下      ");break; //向下
                case 3: lcd_w_word("      向左      ");break; //向左
                case 4: lcd_w_word("      向右      ");break; //向右
                case 5: lcd_w_word("      前进      ");break; //前进
                case 6: lcd_w_word("      后退      ");break; //后退
                case 7: lcd_w_word("    顺时针      ");break; //顺时针
                case 8: lcd_w_word("    逆时针      ");break; //逆时针
          case 9: lcd_w_word("      挥动      ");break; //挥动
                default: lcd_w_word("    未测出      ");break; //逆时针
        }
        lcd_w_char(0,0x98);         //第二行显示起始地址
        lcd_w_word("        爱上电子");//爱上电子
}

//主函数
void main()
{
        Clk_Init();                 //系统时钟初始化
        Port_Init();                //端口初始化
        I2C_Init();                 //硬件I2C初始化
        delay_ms(200);              //等待软硬件系统稳定
        PAJ7620_Init();             //PAJ7620初始化
        Lcd_Init();                 //12864液晶初始化
        while(1)
        {
                Gesture_Get();
                Display();                //12864液晶主屏显示
                delay_ms(200);            //200ms扫描检测一次(可用定时器定时中断操作)
        }
}


使用特权

评论回复
15
无法去污粉| | 2020-10-28 23:28 | 只看该作者
请问,调试DS3231SN芯片的帖子在哪里?不太会找你之前的帖子。或者帖子叫什么?我搜一下。
打扰,楼主分享很棒!

使用特权

评论回复
16
henangongda123|  楼主 | 2020-10-29 09:04 | 只看该作者
无法去污粉 发表于 2020-10-28 23:28
请问,调试DS3231SN芯片的帖子在哪里?不太会找你之前的帖子。或者帖子叫什么?我搜一下。
...

https://bbs.21ic.com/icview-2527734-1-1.html
这个贴里面有源代码

使用特权

评论回复
17
不想画板子| | 2020-10-29 10:25 | 只看该作者
谢谢分享

使用特权

评论回复
18
xiaoqizi| | 2020-11-13 12:36 | 只看该作者
手势识别的原理是什么啊

使用特权

评论回复
19
木木guainv| | 2020-11-13 12:38 | 只看该作者
这个识别芯片大概多少钱啊

使用特权

评论回复
20
磨砂| | 2020-11-13 12:40 | 只看该作者
调试过程顺利吗  遇到什么问题过吗

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

42

主题

2116

帖子

19

粉丝