基于STM32的纸张计数

一.纸张计数所用芯片

我使用的是FDC2214,做完手势识别来做的纸张计数

FDC2214使用代码如下

#include "iic.h" #include "delay.h" //初始化IIC void IIC_Init(void) { GPIO_InitTypeDef GPIO_InitStructure; RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE ); //使能GPIOB时钟 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6|GPIO_Pin_7; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP ; //推挽输出 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_InitStructure); GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7); //PB6,PB7 输出高 } //产生IIC起始信号 void IIC_Start(void) { SDA_OUT(); //sda线输出 IIC_SDA=1; IIC_SCL=1; delay_us(4); IIC_SDA=0;//START:when CLK is high,DATA change form high to low delay_us(4); IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 } //产生IIC停止信号 void IIC_Stop(void) { SDA_OUT();//sda线输出 IIC_SCL=0; IIC_SDA=0;//STOP:when CLK is high DATA change form low to high delay_us(4); IIC_SCL=1; IIC_SDA=1;//发送I2C总线结束信号 delay_us(4); } //等待应答信号到来 //返回值:1,接收应答失败 // 0,接收应答成功 u8 IIC_Wait_Ack(void) { u8 ucErrTime=0; SDA_IN(); //SDA设置为输入 IIC_SDA=1;delay_us(1); IIC_SCL=1;delay_us(1); while(READ_SDA) { ucErrTime++; if(ucErrTime>250) { IIC_Stop(); return 1; } } IIC_SCL=0;//时钟输出0 return 0; } //产生ACK应答 void IIC_Ack(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=0; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; } //不产生ACK应答 void IIC_NAck(void) { IIC_SCL=0; SDA_OUT(); IIC_SDA=1; delay_us(2); IIC_SCL=1; delay_us(2); IIC_SCL=0; } //IIC发送一个字节 //返回从机有无应答 //1,有应答 //0,无应答 void IIC_Send_Byte(u8 txd) { u8 t; SDA_OUT(); IIC_SCL=0;//拉低时钟开始数据传输 for(t=0;t<8;t++) { IIC_SDA=(txd&0x80)>>7; txd<<=1; delay_us(2); //对TEA5767这三个延时都是必须的 IIC_SCL=1; delay_us(2); IIC_SCL=0; delay_us(2); } } //读1个字节,ack=1时,发送ACK,ack=0,发送nACK u8 IIC_Read_Byte(unsigned char ack) { unsigned char i,receive=0; SDA_IN();//SDA设置为输入 for(i=0;i<8;i++ ) { IIC_SCL=0; delay_us(2); IIC_SCL=1; receive<<=1; if(READ_SDA)receive++; delay_us(1); } if (!ack) IIC_NAck();//发送nACK else IIC_Ack(); //发送ACK return receive; }

//这里 FDC_Address_W =0x54 void FDC_write_reg(u8 addr,u16 value) //addr 为寄存器地址,value为需要写入的寄存器数据 { IIC_Start(); //产生START信号 // IIC_Send_Byte(FDC_Address_W); //发送从机地址和写信号 IIC_Send_Byte(0x54); //发送从机地址和写信号 IIC_Wait_Ack(); //等待ACK IIC_Send_Byte(addr); //发送需要写入的寄存器地址 IIC_Wait_Ack(); //等待ACK IIC_Send_Byte(value>>8); //发送高8位数据 IIC_Wait_Ack(); //等待ACK IIC_Send_Byte(value&0xFF); //发送低8位数据 IIC_Wait_Ack(); //等待ACK IIC_Stop(); //产生STOP信号 delay_ms(1); }

//FDC_Address_W =0x54 //FDC_Address_R =0x55 //Receive_Date[] 为接收数据的数组 //C_Data 为一个16位无符号的整型 u16 FDC_read_reg(u8 addr) { u16 Receive_Date[5]; uint16_t C_Data; IIC_Start(); //产生START信号 // IIC_Send_Byte(FDC_Address_W); //发送写命令 IIC_Send_Byte(0x54); //发送写命令 IIC_Wait_Ack(); IIC_Send_Byte(addr); //发送需要读的寄存器的地址 IIC_Wait_Ack(); IIC_Start(); // IIC_Send_Byte(FDC_Address_R); //发送读命令 IIC_Send_Byte(0x55); //发送读命令 IIC_Wait_Ack(); Receive_Date[0]=IIC_Read_Byte(1); //读高8位 Receive_Date[1]=IIC_Read_Byte(0); //读低8位 IIC_Stop(); //产生STOP信号 C_Data=(Receive_Date[0]<<8)+ Receive_Date[1]; return C_Data; }

u8 FDC_read_reg_high(u8 addr) { u16 FDC_Address_W =0x54; u16 FDC_Address_R =0x55; u16 Receive_Date[5]; IIC_Start(); IIC_Send_Byte(FDC_Address_W); IIC_Wait_Ack(); IIC_Send_Byte(addr); IIC_Wait_Ack(); IIC_Start(); IIC_Send_Byte(FDC_Address_R); IIC_Wait_Ack(); Receive_Date[0]=IIC_Read_Byte(1); Receive_Date[1]=IIC_Read_Byte(0); IIC_Stop(); return Receive_Date[0]; }

u8 FDC_read_reg_low(u8 addr) { u16 FDC_Address_W =0x54; u16 FDC_Address_R =0x55; u16 Receive_Date[5]; IIC_Start(); IIC_Send_Byte(FDC_Address_W); IIC_Wait_Ack(); IIC_Send_Byte(addr); IIC_Wait_Ack(); IIC_Start(); IIC_Send_Byte(FDC_Address_R); IIC_Wait_Ack(); Receive_Date[0]=IIC_Read_Byte(1); Receive_Date[1]=IIC_Read_Byte(0); IIC_Stop(); return Receive_Date[1]; }

void FDC_Start(void) { FDC_write_reg(0x08,0x8329); //(CHx_RCOUNT*16)/55M ==9.76ms,,每10ms左右可以读一次值 FDC_write_reg(0x09,0x8329); FDC_write_reg(0x0A,0x8329); FDC_write_reg(0x0B,0x8329); FDC_write_reg(0x10,0x000A); //设置4个通道最小稳定时间 FDC_write_reg(0x11,0x000A); FDC_write_reg(0x12,0x000A); FDC_write_reg(0x13,0x000A); FDC_write_reg(0x14,0x1001); //时钟除以1,设置传感器频率在0.01M到8.5M之间 FDC_write_reg(0x15,0x1001); FDC_write_reg(0x16,0x1001); FDC_write_reg(0x17,0x1001); FDC_write_reg(0x19,0x0000); //不设置中断标志位 FDC_write_reg(0x1B,0xC20D);//使能0,1,2,3通道,且带宽设置为10M FDC_write_reg(0x1E,0x8000); //设置4个通道的驱动电流 FDC_write_reg(0x1F,0x8000); FDC_write_reg(0x20,0x8000); FDC_write_reg(0x21,0x8000); FDC_write_reg(0x1A,0x1401); //使能FDC2214,且取内部时钟为参考时钟 }

//IO方向设置 #define SDA_IN() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)8<<28;} #define SDA_OUT() {GPIOB->CRL&=0X0FFFFFFF;GPIOB->CRL|=(u32)3<<28;}

//IO操作函数 #define IIC_SCL PBout(6) //SCL #define IIC_SDA PBout(7) //SDA #define READ_SDA PBin(7) //输入SDA

//IIC所有操作函数 void IIC_Init(void); //初始化IIC的IO口 void IIC_Start(void); //发送IIC开始信号 void IIC_Stop(void); //发送IIC停止信号 void IIC_Send_Byte(u8 txd); //IIC发送一个字节 u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节 u8 IIC_Wait_Ack(void); //IIC等待ACK信号 void IIC_Ack(void); //IIC发送ACK信号 void IIC_NAck(void); //IIC不发送ACK信号

void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data); u8 IIC_Read_One_Byte(u8 daddr,u8 addr);

void FDC_write_reg(u8 addr,u16 value); void FDC_Start(void); u16 FDC_read_reg(u8 addr); u8 FDC_read_reg_high(u8 addr); u8 FDC_read_reg_low(u8 addr);

#endif

二:如何计数纸张

FDC2214芯片会根据电容大小,也可以说是纸张厚度,反馈一个数值,我使用的是CH3通道,

为了使得值稳定,我所想有俩种方法,第一种是获得1W个值,然后取平均值,简单粗暴

第二种,连续获得1111个数,分成100份,取100份的中位数,然后取平均值,这个比第一种稳定

主函数代码如下

C_CH3_data=FDC_read_reg(0x06)&0xFFF; C_CH3_data_low = FDC_read_reg(0x07); for(i=0;i<=9999;i++) { C_CH3_data_final =((C_CH3_data<<16)+C_CH3_data_low); C_CH3_data_final=C_CH3_data_final/100000; CH3=CH3+C_CH3_data_final; } C_CH3_data_final=CH3/10000; CH3=0;

LCD_ShowxNum(100,70,C_CH3_data_final,9,24,0);

根据显示屏上显示的数,记录1,2,3,4,5......张纸的数据进行数据拟合

三:数据拟合

使用excel可以进行数据拟合,根据个人的数据情况进行分别拟合,我这里随便来10位数模拟一下

选择散点图,趋势线,更多选项

可以看到可以显示公式

看个人代码设置选择公式,一般选择多项式

之后,设置一个烧入数据,比如说烧入第一张纸的数据,如果烧入的为1,之前所获得的公式第一张纸为5,则将公式的y值加4

经验分享 程序员 微信小程序 职场和发展