- 本文記錄將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觸控板的驅動程序就移植完成了,下面給出完整的工程文件。
文章不足之處歡迎提出建議。