- 本文记录将arduino下的mpr121触摸板驱动程序移植到stam32f1
1、触摸板简介
之前买了块mpr121做主控的触摸控制板(如下图),卖家给的驱动是arduino的,最近做项目需要移植到stm32上。看了mpr121的用户指南和arduino的程序,其实要移植要做的工作也不是特别多,主要就是iic和外部中断。
2、移植思路
mpr121采用中断方式与MCU通信,协议用的是IIC。移植要做的就是做好stm32的iic和外部中断初始化,然后按照mpr121的通信要求封装IIC的几个基本函数就行了,步骤整理如下。
1、stm32下的外部中断配置和IIC通信配置
2、按照用户手册的要求封装基本的IIC函数
3、移植初始化函数和按键读取函数
3、移植
3.1、stm32的IIC和外部中断配置
这里问我直接用的正点原子的模拟IIC例程和野火的外部中断例程。外部中断初始化后就是中断服务函数的编写,这个放在后面按键读取函数那说明。IIC使用模拟的,这个点原子的例程里提供了几个基本的通信函数。
//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信号
其中IIC_Read_Byte(unsigned char ack)
这个函数的说明如下
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(unsigned char ack)
3.2、IIC通信函数的封装
卖家给的例程里跟mpr121通信的函数有两个,一个是 unsigned char mpr121Read(uint8_t address)
另一个是void mpr121Write(unsigned char address, unsigned char data)
(这里原本的程序读取是分成两步的,我把它合并成一步了)。我们只要按照mpr121的通信要求实现这两个函数就行。
这里我对stm32的iic通信函数又做了一次封装为的是少打代码(笑),如下:
/*********************
****I2C Functions****
*********************/
void i2cInit(void) //iic引脚初始化
{
IIC_Init();
}
void i2cSendStart(void) // 发送起始信号
{
IIC_Start();
}
void i2cSendStop(void) //发送终止信号
{
// transmit stop condition
IIC_Stop();
}
void i2cWaitForComplete(void) //等待应答信号
{
IIC_Wait_Ack();
}
void i2cSendByte(unsigned char data) //发送一个字节
{
IIC_Send_Byte(data);
}
unsigned char i2cReceiveByte(unsigned char ackFlag) //接收一个字节,ackFlag=1:应答,ackFlag=0:不应答
{
return IIC_Read_Byte(ackFlag);
}
·发送函数
mpr121用户手册中接收数据的时序要求如下图
例程中使用的是上图第二个通信时序,我们照搬就成,程序实现如下
void mpr121Write(unsigned char address, unsigned char data)
{
i2cSendStart();
i2cSendByte(MPR121_W);// write 0xB4
i2cWaitForComplete();
i2cSendByte(address); // write register address
i2cWaitForComplete();
i2cSendByte(data);
i2cWaitForComplete();
i2cSendStop();
}
- 读取函数
mpr121用户手册中对读取的时序要求如下
函数实现如下
unsigned char mpr121Read(uint8_t address)
{
unsigned char data;
i2cSendStart();
i2cSendByte(MPR121_W); // write 0xB4
i2cWaitForComplete();
i2cSendByte(address); // write register address
i2cWaitForComplete();
i2cSendStart();
i2cSendByte(MPR121_R); // write 0xB5
i2cWaitForComplete();
data =i2cReceiveByte(TRUE);
i2cSendStop();
return data;
}
其中有关地址的说明如下,设备地址左移一位,最低位为1或0表示读或者写(具体参考mpr121数据手册)
#define MPR121_R 0xB5 // ADD pin is grounded
#define MPR121_W 0xB4 // So address is 0x5A
3.3、 移植初始化函数和按键读取函数
实现了与mpr121通信之后就是对mpr121的初始化了,这里我直接用的例程中的初始化序列,如果是后期自己做touchpad的话还得自己去看数据手册并且更改相应的参数,这里就不深入探讨了。
- 初始化序列如下(外部中断的初始化也放在了这里)
void mpr121QuickConfig(void)
{
mpr121_irqInit();//interrupt set
// Section A
// This group controls filtering when data is > baseline.
mpr121Write(MHD_R, 0x01);
mpr121Write(NHD_R, 0x01);
mpr121Write(NCL_R, 0x00);
mpr121Write(FDL_R, 0x00);
// Section B
// This group controls filtering when data is < baseline.
mpr121Write(MHD_F, 0x01);
mpr121Write(NHD_F, 0x01);
mpr121Write(NCL_F, 0xFF);
mpr121Write(FDL_F, 0x02);
// Section C
// This group sets touch and release thresholds for each electrode
mpr121Write(ELE0_T, TOU_THRESH);
mpr121Write(ELE0_R, REL_THRESH);
mpr121Write(ELE1_T, TOU_THRESH);
mpr121Write(ELE1_R, REL_THRESH);
mpr121Write(ELE2_T, TOU_THRESH);
mpr121Write(ELE2_R, REL_THRESH);
mpr121Write(ELE3_T, TOU_THRESH);
mpr121Write(ELE3_R, REL_THRESH);
mpr121Write(ELE4_T, TOU_THRESH);
mpr121Write(ELE4_R, REL_THRESH);
mpr121Write(ELE5_T, TOU_THRESH);
mpr121Write(ELE5_R, REL_THRESH);
mpr121Write(ELE6_T, TOU_THRESH);
mpr121Write(ELE6_R, REL_THRESH);
mpr121Write(ELE7_T, TOU_THRESH);
mpr121Write(ELE7_R, REL_THRESH);
mpr121Write(ELE8_T, TOU_THRESH);
mpr121Write(ELE8_R, REL_THRESH);
mpr121Write(ELE9_T, TOU_THRESH);
mpr121Write(ELE9_R, REL_THRESH);
mpr121Write(ELE10_T, TOU_THRESH);
mpr121Write(ELE10_R, REL_THRESH);
mpr121Write(ELE11_T, TOU_THRESH);
mpr121Write(ELE11_R, REL_THRESH);
// Section D
// Set the Filter Configuration
// Set ESI2
mpr121Write(FIL_CFG, 0x04);
// Section E
// Electrode Configuration
// Enable 6 Electrodes and set to run mode
// Set ELE_CFG to 0x00 to return to standby mode
mpr121Write(ELE_CFG, 0x0C); // Enables all 12 Electrodes
//mpr121Write(ELE_CFG, 0x06); // Enable first 6 electrodes
// Section F
// Enable Auto Config and auto Reconfig
/*mpr121Write(ATO_CFG0, 0x0B);
mpr121Write(ATO_CFGU, 0xC9); // USL = (Vdd-0.7)/vdd*256 = 0xC9 @3.3V mpr121Write(ATO_CFGL, 0x82); // LSL = 0.65*USL = 0x82 @3.3V
mpr121Write(ATO_CFGT, 0xB5);*/ // Target = 0.9*USL = 0xB5 @3.3V
}
然后就是按键读取了,这里的思路是在外部中断的中断服务程序中将按键标志置位,然后在主程序中检测到标志置位后再读取键值,当然也可以在中断服务程序中就完成这些工作。代码如下
- 中断服务程序
void KEY1_IRQHandler(void)
{
//确保是否产生了EXTI Line中断
if(EXTI_GetITStatus(KEY1_INT_EXTI_LINE) != RESET)
{
key_pressed=0;
LED0=!LED0;
//清除中断标志位
EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);
}
}
- 键值读取
char getPhoneNumber()
{
int touchNumber;
int j;
uint16_t touchstatus;
char key=-1;
//Serial.println("Please Enter a phone number...");
//while(key_pressed);//用while读取会阻塞程序运行
if(key_pressed==0)//非阻塞方式
{
key_pressed=1;
touchNumber = 0;
touchstatus = mpr121Read(0x01) << 8;
touchstatus |= mpr121Read(0x00);
for (j=0; j<12; j++) // Check how many electrodes were pressed
{
if ((touchstatus & (1<<j)))
touchNumber++;
}
if (touchNumber == 1)
{
if (touchstatus & (1<<STAR))
key = '*';
else if (touchstatus & (1<<SEVEN))
key = '7';
else if (touchstatus & (1<<FOUR))
key= '4';
else if (touchstatus & (1<<ONE))
key = '1';
else if (touchstatus & (1<<ZERO))
key= '0';
else if (touchstatus & (1<<EIGHT))
key = '8';
else if (touchstatus & (1<<FIVE))
key = '5';
else if (touchstatus & (1<<TWO))
key = '2';
else if (touchstatus & (1<<POUND))
key = '#';
else if (touchstatus & (1<<NINE))
key = '9';
else if (touchstatus & (1<<SIX))
key = '6';
else if (touchstatus & (1<<THREE))
key = '3';
//Serial.print(key[i]);
}
else if (touchNumber == 0);
else;
//Serial.println("Only touch ONE button!");
}
return key;
}
到这里mpr121触控板的驱动程序就移植完成了,下面给出完整的工程文件。
文章不足之处欢迎提出建议。