stm32f4

STM32F4 VE MPR121 İLE KAPASİTİF DOKUNMA ALGILAMA

MPR121 entegresi kapasitif dokunmaları tespit etmek ve kapasite değerlerini okumak için tasarlanmıştır. MPR121 entegresinin 12 dokunma algılayıcı kanalı ve bir yakınlık tespit kanalı bulunmaktadır. MPR121 entegresi ve STM32F4  mikrodenetleyicisi arasında oluşturulan I2C arayüzü haberleşmesiyle kapasitif sensörün kanalların dokunma durumunu tespit edilmesini anlatacağım.

MPR121 entegresinin Vin bacağı 3.3V’a, GND toprak’a, SDA bacağı STM32F407’nin B portunun 7. pinine, SCLbacağı STM32F407’nin B portunun 6. pinine bağlanmıştır. ADDR bacağı ise boşta bırakılmıştır.

MPR121 dataheet sayfası

AN3892 Uygılama Notu

AN3891 Uygulama Notu

İlk olarak kullandığımız mikrodenetleyiciyi tanıtan kütüphaneyi,ardından Sprintf fonksiyonunu kullanmak için stdio.h kütüphanesini,boolean ifadelerini kullanmak için stdbool.h kütüphanelerini ekledik.

#include "stm32f4xx.h"                  // Device header
#include <stdio.h>
#include <stdbool.h>

Aşağıdaki int j değişkenini dokunma tespit edilen kanalların numarasını UART ile yazdıran bir for döngüsü için kullandım. char str[50] değişkenini UART mesajını taşıması için ekledim. Benim kullandığım MPR121 entegresi için hazırlanmış board üzerinde ADDR girişi VCC ile doğrudan bağlı olduğundan  datasheet’in 24. sayfasındaki tablo 7’ye göre I2C haberleşmesi için kullanılacak adresi 0x5A olur.MPR121_I2CADDR_DEFAULT bu adresi tutar. MPR121 entegresinde kanalların dokunma durumlarını 0x00, ve 0x01 adreslerindeki iki 8 bitlik registerlar tutarlar. Bu registerlarda her kanala denk gelen bit dokunulduğunda 0x00 adresini MPR121_TOUCHSTATUS_L tanımlamaktadır ve 0’dan 7. kanala kadar her biti bir kanalın dokunma durumunu gösterir(LSB 0 , MSB 7 ). 0x01 adresini MPR121_TOUCHSTATUS_H tanımlamaktadır. Bu register LSB 8. Kanaldan başlayıp 11 kanal 4. bitte tutar.. 12. kanal ise son kanal biti olup dokunma durumunu değil yakınlık tespit durumunu verir. Bu biti sıfır olarak kullanacağız. Kalan diğer iki bit sürekli sıfır olup MSB ise aşırı kanal akımı oluştuğunda 1 olur. Bu tanımlamaların ardından bu kontrol ve konfigürasyon registerlarının adreslerini tanımladık. Bu adreslerdeki kaydedicilerin, dokunma tespitini doğru yapması için bu kaydedicilerin konfigürasyonunun iyi bir şekilde yapılması gerekmektedir. Son olarak 0x80 adresindeki yazılım reset registerı bulunur bu register adresi MPR121_SOFTRESET ile tanımlanmıştır.

int j;
char str[50];
#define MPR121_I2CADDR_DEFAULT 0x5A
#define MPR121_TOUCHSTATUS_L 0x00
#define MPR121_TOUCHSTATUS_H 0x01
#define MPR121_MHDR         0x2B
#define MPR121_NHDR         0x2C
#define MPR121_NCLR         0x2D
#define MPR121_FDLR         0x2E
#define MPR121_MHDF         0x2F
#define MPR121_NHDF         0x30
#define MPR121_NCLF         0x31
#define MPR121_FDLF         0x32
#define MPR121_NHDT         0x33
#define MPR121_NCLT         0x34
#define MPR121_FDLT         0x35

#define MPR121_TOUCHTH_0    0x41
#define MPR121_RELEASETH_0    0x42
#define MPR121_DEBOUNCE 0x5B
#define MPR121_CONFIG1 0x5C
#define MPR121_CONFIG2 0x5D

#define MPR121_ECR 0x5E

#define MPR121_SOFTRESET 0x80

Aşağıda USART ile veri göndermek için USART_Puts fonksiyonu tanımladım. Bu fonksiyon giriş olarak USART adını ve pointer karakter dizisinin başlangıç adresini alır. Karakter dizilerinin son elemanının null olduğu için bütün dizi elemanları sıfırıncı elemandan itibaren while döngüsü döner. while(!(USARTx->SR&0x00000040)); komutu ile USART meşgul olduğunda durmayı sağlar. USART meşgul değilse her karakter elemanı ayrı ayrı gönderilir ve adres arttırılarak sonraki karaktere geçilir.

void USART_Puts(USART_TypeDef* USARTx, volatile char *s){
while(*s){
while(!(USARTx->SR&0x00000040));
USART_SendData(USARTx,*s);
*s++;
}
}

Aşağıdaki fonksiyonu 12 kanalın dokunma durumlarını gösteren decimal formatta olan sayının çözümlenmesinin ardından sıfırıncı kanalın durumunun dizinin sıfırıncı elemanına gelecek şekilde tüm kanal durumlarının tüm dizi elemanlarına atanmasını sağlar.

void BinaryToDigit(uint16_t gelen,int diziadi[]){
int c,i;
int k;
for(c=11;c>=0;c--){
k=gelen>>c;
if(k&1){
diziadi[c]=1;
}else{
diziadi[c]=0;
}

}
}

Aşağıdaki fonksiyonla I2C haberleşmesi için kullanılacak olan I2C1 için gerekli olan tanımlamalar yapılmıştır. I2C1 SCL için B6 pinini,SDA için ise B7 pinini kullanmaktadır. Bundan dolayı hem I2C ayarları için hem de GPIO ayarlarının yapılması gerekmektedir. GPIO_InitTypeDef  yapı özelliklerini GPIO_InitStructure’a atadım. I2C_InitTypeDef yapı özelliklerini ise I2C_InitStructure’a atadım. Ardından I2C1 ve GPIOB portuna ait clock hatları aktif edilmiştir. Aktif edilmeseydi her iki yapı da kullanılamazdı. Ardından GPIO_Pin_6 ve GPIO_Pin_7 Alternatif fonksiyon modunda, 50MHz hızında,Open Drain çıkış tipinde, Pull-up kanal yapısında olduğunu belirtip B portuna atadık. GPIO_PinAFConfig komutu ile B6 ve B7 pinlerinin I2C1 için tanımlanmış yapılarında kullanılacağının ayarı yapılmıştır. Ardından I2C yapısının hızı için 100KHz ayarı yapılmıştır. I2C yapısı için modu I2C olarak belirtilmiş ve I2C’nin DutyCycle için I2C_DutyCycle_2 kullanılmıştır. Bunun anlamı bir sinyal periyodunda LOW periyot HIGH periyodun iki katı olacaktır. I2C’nin adresi 0 yapılmıştır. Bu adres default adrestir. Ardından I2C’nin acknowledge yapması aktif edildi. Ardından slave olarak kullanılacak adresin 7 bit olarak kullanılacağı bildirilmiştir. 8 bitlik haberleşmede 1 bit transmit veya recieve  biti olarak kullanılacaktır . Ardından I2C yapısındaki bilgiler I2C1’e aktarılmıştır ve son olarak I2C1 aktif edilmiştir.

void Init_I2C1(void){
GPIO_InitTypeDef GPIO_InitStructure;
I2C_InitTypeDef I2C_InitStructure;


RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);


//SCL  PB6 ,SDA PB7 

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; 
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; 
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; 
GPIO_InitStructure.GPIO_OType = GPIO_OType_OD; 
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; 
GPIO_Init(GPIOB, &GPIO_InitStructure); 


GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_I2C1); // SCL
GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_I2C1); // SDA


I2C_InitStructure.I2C_ClockSpeed = 100000; 
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C; 
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2; 
I2C_InitStructure.I2C_OwnAddress1 = 0; 
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable; 
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit; 
I2C_Init(I2C1, &I2C_InitStructure); 


I2C_Cmd(I2C1, ENABLE);
}

Aşağıdaki fonksiyon I2C haberleşmesini başlatmak için tanımlanmış olunup,  bu fonksiyonun direction kısmına eğer okuma yapılacaksa “I2C_Direction_Receiver”, yazma yapılacaksa “I2C_Direction_Transmitter ” yazılmalıdır. Bu fonksiyon ilk olarak I2C hattının meşgul olup olmamasına bakar meşgulse bekler, değilse devam eder. Ardından I2C_GenerateSTART ile start sinyali üretilir. Ardından bu start koşulunun doğru bir şekilde üretildiğinin bilgisi alınana kadar beklenir. Ardından slave cihazın adresi ve okuma (receive ) /yazma(transmitt) durumu direction olarak gönderilir. Ardından slave  adresini doğrulayan acknowledge gönderir ve Eğer direction “I2C_Direction_Transmitter ” ise I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED event bilgisi 1 yapılır. Aksi halde 0 olur. Eğer direction “I2C_Direction_Receiver ” ise I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED event bilgisi 1 yapılır. Aksi halde o da 0 olur.  Bunlar göz önüne alındığında start koşulunun acknowledge bilgisi alınana kadar beklenir.

void I2C_start(I2C_TypeDef* I2Cx, uint8_t adres, uint8_t direction) {

while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY));
 
I2C_GenerateSTART(I2Cx, ENABLE);

while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)); 
I2C_Send7bitAddress(I2Cx, adres, direction); 
if(direction == I2C_Direction_Transmitter){
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
}
else if(direction == I2C_Direction_Receiver){
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
}
}

Aşağıdaki fonksiyon bir bytelık veri göndermektedir. Giriş olarak I2C adı ve datayı alır. I2C_SendData ile veri gönderilir. İlgili verinin tamamı gönderilince “I2C_EVENT_MASTER_BYTE_TRANSMITTED” eventi set olur. Bu fonksiyonda tüm veri gönderiline kadar beklenir.

void I2C_write(I2C_TypeDef* I2Cx, uint8_t data)
{ 
I2C_SendData(I2Cx, data);
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
}

Aşağıdaki fonksiyon slave cihazdan data alıp ,data alındığında ise  acknowledge bilgisi göndermez. Bu fonksiyon stop sinyali ürettikten sonra  slave cihazdan gelen byte’ın tamamı alınana kadar bekler ve bu datayı döndürür.

uint8_t I2C_read_nack(I2C_TypeDef* I2Cx){
I2C_AcknowledgeConfig(I2Cx, DISABLE);
I2C_GenerateSTOP(I2Cx, ENABLE);
while( !I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) );
uint8_t data = I2C_ReceiveData(I2Cx);
return data;
}

Aşağıdaki fonksiyon slave cihazdan data alıp , data alındığında ise  acknowledge bilgisi gönderir. Bunun için Acknowledge aktif edilir. Bu fonksiyon slave cihazdan gelen byte’ın tamamı alınana kadar bekler ve bu datayı döndürür.

uint8_t I2C_read_ack(I2C_TypeDef* I2Cx){
I2C_AcknowledgeConfig(I2Cx, ENABLE);
while( !I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED) );
uint8_t data = I2C_ReceiveData(I2Cx);
return data;
}

Aşağıdaki fonksiyon stop sinyali üretir.

void I2C_stop(I2C_TypeDef* I2Cx){
I2C_GenerateSTOP(I2Cx,ENABLE);
}

Aşağıdaki fonksiyon bir register’a veri yazmaktadır. Bu fonksiyon I2C adı, register adresi ve register’a yazılacak değeri giriş olarak almaktadır. MPR121 entegresinin datasheetinde de belirtilen formatta önce start komutu slave cihazın adresi bir bit sola kaydırılarak gönderilir ardından I2C_write komutuyla register adresi gönderilir. Ardından I2C_write komutuyla registera yazılacak değer gönderilir.

write format mpr121

void writeRegister(I2C_TypeDef* I2Cx, uint8_t register_adresi,uint8_t deger)
{
I2C_start(I2Cx,MPR121_I2CADDR_DEFAULT<<1,I2C_Direction_Transmitter);
I2C_write(I2Cx,register_adresi);
I2C_write(I2Cx,deger);
I2C_stop(I2Cx);
}

MPR121 entegresinde tek bir byte okunacağı vakit önce start komutu verilir sonra, register adresinin I2C_write komutuyla yazılır ardından restart edilmesi ve acknowledge almadan okumayı yapıp değer döndürülür. MPR121 entegresinde okuma sırasında register adresleri otomatik olarak artar o yüzden sadece ilk registerı okuyup sonraki registerı okumayacağım zaman aşağıdaki fonksiyonu kullandım.

read format.jpg

uint8_t I2C_readreg(I2C_TypeDef* I2Cx,uint8_t devAddr, uint8_t regAddr)
{
I2C_start(I2Cx, devAddr<<1,I2C_Direction_Transmitter);
I2C_write(I2Cx,regAddr); 
I2C_stop(I2Cx);
I2C_start(I2Cx, devAddr<<1,I2C_Direction_Receiver);
return I2C_read_nack(I2Cx);
}

Ardarda iki byte okunup ikinci byte elde etmek istediğim durumlarda aşağıdaki fonksiyonu kullandım. Burada okunmak istenen registerdan bir önceki registerın adresi girilir ve bu registerın üstü eğer okunuyorsa alınan değer girilen register adresinin bir üst adresinden alınmış olunur.

uint8_t I2C_readreg2(I2C_TypeDef* I2Cx,uint8_t devAddr, uint8_t regAddr)
{
I2C_start(I2Cx, devAddr<<1,I2C_Direction_Transmitter);
I2C_write(I2Cx,regAddr); 
I2C_stop(I2Cx);
I2C_start(I2Cx, devAddr<<1,I2C_Direction_Receiver);
uint8_t a=I2C_read_ack(I2Cx);
uint8_t b=I2C_read_nack(I2Cx);
return b;
}

aşağıdaki fonksiyon sıfırıncı. kanaldan 11. kanala kadar her kanala aynı touch ve release threshold değerini atar. Eğer baseline -filtre edilmiş data değeri touch thresholddan büyükse touch status registerlardaki dokunma biti 1 yapılır. Eğer baseline -filtre edilmiş data değeri release  thresholddan küçükse bit sıfır yapılır.

void setThresholds(I2C_TypeDef * I2Cx,uint8_t touch, uint8_t release) {
for (uint8_t i=0; i<12; i++) {
writeRegister(I2Cx,MPR121_TOUCHTH_0 + 2*i, touch);
writeRegister(I2Cx,MPR121_RELEASETH_0 + 2*i, release);
}
}

Aşağıdaki fonksiyon dokunulan pinlere göre bir decimal sayı üretmektedir. örneğin 0. pine dokunursak 1,1.pine dokunursak 2, 11. pine dokunursak 2048 sayısını verir. Bu decimal sayıyı sonra BinaryTo Decimal fonksiyonu ile çözümleyip hangi pinlere dokunulduğunu göreceğiz. fonksiyonda son olarak verilecek olan touched_flag’a 0x00 değeri atanır. Ardından önce 0. kanaldan ile 7. kanal dahil ilk sekiz kanala ait dokunma dğerini içeren 0x00 adresindeki register okunur ve t değişkenine atanır sonra da touched_flag değişkenine atanır. Ardından 0x01 registerından okunan data t değişkenine atanır. 0x01 adresindeki kaydedicilerde yalnız dokunma durumunu gösteren bitler bulunmaz bu sebeble sadece işe yarayanları almak için t değişkenini ilk dört biti 1 olan 0x1F=00001111 ile and işlemine tabi tutup sonra tekrar t değişkenine atarız. son olarak elde olan iki byte birleştirmek için t sekiz bit sola kaydırılır ve touched _flag ile or işlemine tabi tutulur. Sonuc tekrar touched_flag değişkenine atanır.

uint16_t touched(void) {
uint16_t touched_flag=0x00;
uint8_t t = I2C_readreg(I2C1,MPR121_I2CADDR_DEFAULT,0x00);
touched_flag=t;
t = I2C_readreg2(I2C1,MPR121_I2CADDR_DEFAULT,0x00);
t= t&0x1F;
touched_flag= touched_flag|(t<< 8);
return touched_flag;
}

Aşağıdaki fonksiyon main fonksiyonunun içine dahil edilip hem I2C1 ayarları yapılıp I2C1 başlatılacak hem de mpr121 konfigürasyon ayarları için konfigürasyon registerlarına default değer atanmıştır.Bu fonksiyonda I2C1 ayarları dahil edilip başladıktan sonra mpr121 entegresine yazılımsal rest atılmıştır.

void begin(void) {
Init_I2C1();

// soft reset
writeRegister(I2C1,MPR121_SOFTRESET,0x63);

writeRegister(I2C1,MPR121_ECR, 0x00);

setThresholds(I2C1,12, 6);
writeRegister(I2C1,MPR121_MHDR, 0x01);
writeRegister(I2C1,MPR121_NHDR, 0x01);
writeRegister(I2C1,MPR121_NCLR, 0x0E);
writeRegister(I2C1,MPR121_FDLR, 0x00);
writeRegister(I2C1,MPR121_MHDF, 0x01);
writeRegister(I2C1,MPR121_NHDF, 0x05);
writeRegister(I2C1,MPR121_NCLF, 0x01);
writeRegister(I2C1,MPR121_FDLF, 0x00);
writeRegister(I2C1,MPR121_NHDT, 0x00);
writeRegister(I2C1,MPR121_NCLT, 0x00);
writeRegister(I2C1,MPR121_FDLT, 0x00);
writeRegister(I2C1,MPR121_DEBOUNCE, 0x00);
writeRegister(I2C1,MPR121_CONFIG1, 0x10); 
writeRegister(I2C1,MPR121_CONFIG2, 0x20);

writeRegister(I2C1,MPR121_ECR, 0x8F);

}

Aşağıda main fonksiyonu içinde ilk olarak A portunun 2. pininin USART2 için Tx olarak kullanılmasına imkan veren bir ayarlamalar yapılmıştır. USART2 aktif edilmiştir. Yukarıda özellikleri belirtilen begin fonksiyonu dahil edilmiştir. Ardından kanallar[12] adlı dizi tanımlanmıştır. Bu dizide dizi elemanının sırasına sıradaki denk gelen kanalın durumu eğer dokunma varsa 1 , dokunma yoksa 0 yazılır. Programın sürekli çalışması için while döngüsü tanımlanmıştır. BinaryToDigit fonksiyonuna touched fonksiyonuna ve kanallar dizisi giriş olarak verilmiştir. Ardından sırayla 12 kanalında dokunma durumlarını okuyan bir fonksiyon tanımlanmıştır. Bu fonksiyon kanallar dizisinin hangi elemanı 1 ise o elemanın sırasının bir fazlasını dokunuldu şeklinde USART ile yazdırmaktadır. Son olarak bekleme sağlamak amacıyla while deyimi oluşturulmuştur.

int main(){
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType=GPIO_OType_OD;
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_2;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource2, GPIO_AF_USART2);
USART_InitStructure.USART_BaudRate=115200;
USART_InitStructure.USART_HardwareFlowControl=USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode=USART_Mode_Tx;
USART_InitStructure.USART_Parity=USART_Parity_No;
USART_InitStructure.USART_StopBits=USART_StopBits_1;
USART_InitStructure.USART_WordLength=USART_WordLength_8b;
USART_Init(USART2,&USART_InitStructure);
USART_Cmd(USART2,ENABLE); 
begin();
int kanallar[12];
while(1){
BinaryToDigit(touched(),kanallar);
for(int j=0;j<12;j++){
if(kanallar[j]==1){
sprintf(str,"%d dokundu\n",j+1);
USART_Puts(USART2,str);
}
int z=60000;
while(z--);
}
}
}

e-mail: fatih22alparslan@gmail.com

Ücret karşılığı proje için whatsapp:

https://wa.me/message/4DTQQTTMLPFWC1

STM32F4 VE MPR121 İLE KAPASİTİF DOKUNMA ALGILAMA&rdquo için 1 yorum

Bir Cevap Yazın

Aşağıya bilgilerinizi girin veya oturum açmak için bir simgeye tıklayın:

WordPress.com Logosu

WordPress.com hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Google fotoğrafı

Google hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Twitter resmi

Twitter hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Facebook fotoğrafı

Facebook hesabınızı kullanarak yorum yapıyorsunuz. Çıkış  Yap /  Değiştir )

Connecting to %s

%d blogcu bunu beğendi: