STM32F4 ve StdPeriph ile CANBUS Interrupt kullanımı
Herkese merhabalar, bu yazımızda sizlere STM32F4 serisi mikro işlemciler ile CAN BUS haberleşmeden bahsedeğiz. CAN BUS haberleşmeyi Interrupt kullanarak ve Std Periph kütüphanesi kullanarak yapacağız. Uygulamada STM32F4-Discovery kartını kullanacağız.Şimdi ilk olarak Basitçe CAN BUS nedir sorusunun cevabı ile başlayalım.
CAN teknolojisi Bosch otomotiv tarafından, araçlardaki kablo yükünü azaltmak ve araçtaki sistemleri birbirinden ayırmak için tasarlanmıştır. Eskiden eğer bir sensörde bir hata oluşursa, bu hata bütün sistemlerin haberleşmesini etkilemekteydi. CAN BUS ile bütün sistemler birbirinden ayrılmış durumda ve bireysel olarak çalışmaktadırlar. Haberleşmek için ise verilerini CAN hattına göndermektedirler. Bu sayede bir arıza oluğunda sadece o sensörde hata meydana geliyor ve diğerlerini etkilemiyor.
CANBUS protokolünde hata ayıklama işlemi çok daha basit durumdadır. Çünkü herbir cihazın kendine ait bir kimlik bilgisi olur ve bu sayede arızanın kaynağını rahatça bulabilmekteyiz. Cihazlar CAN BUS hattına verilerini yüklerken bu kimlik bilgileriyle yüklemektedir.
Hadi biraz şov yapalım ve kodlama kısmına geçelim.
İlk olarak veri gönderebilmek ve alabilmek için gerekli değişkenlerimizi ve yapılarımızı oluşturalım.
//CAN BUS cihaz id numarası
uint16_t canbus_device_id = 0xaf;
// Can Transmit Message Nesnesi
CanTxMsg TxMessage;
// Can Receive Message Nesnesi
CanRxMsg RxMessage;
//Aşağıdaki verileri canbus verisini tutmak için kullanacağız
char canbusData_id[2];
char canbusData_0 [2];
char canbusData_1 [2];
char canbusData_2 [2];
char canbusData_3 [2];
char canbusData_4 [2];
char canbusData_5 [2];
char canbusData_6 [2];
char canbusData_7 [2];
Aşağıdaki kod bloğunda bu işlemi görebilirsiniz :
void CanWriteData( uint16_t canbus_device_id, uint16_t d0, uint16_t d1, uint16_t d2, uint16_t d3, uint16_t d4, uint16_t d5, uint16_t d6, uint16_t d7){
//İlk olarak can bus cihaz id değerini ve standart canbus haberleşmesini belirledik
TxMessage.StdId = canbus_device_id;
TxMessage.RTR = CAN_RTR_DATA;
TxMessage.IDE = CAN_ID_STD;
//Daha sonra kaçtane mesaj paketi göndereceğimizi belirledik.
TxMessage.DLC = 8;
//Gönderilecek CANBUS verilerini Can Transmit Message yapısına yükledik.
TxMessage.Data[0] = d0;
TxMessage.Data[1] = d1;
TxMessage.Data[2] = d2;
TxMessage.Data[3] = d3;
TxMessage.Data[4] = d4;
TxMessage.Data[5] = d5;
TxMessage.Data[6] = d6;
TxMessage.Data[7] = d7;
//Son olarak yukarıda hazırladığımız mesaj paketini gönderdik.
//Bu metodla birlikte oluşturulan mesaj paketi CAN hattında yüklenecektir.
CAN_Transmit(CAN1,&TxMessage);
}
Yukarıda gördüğünüz üzere transmit için gereken metodumuzu yazdık.
Yazının başında dediğim gibi bu uygulamada interrupt kullanacağız. CAN verisini interrupt kullanarak alacağız ve ilgili Receive Message değişkenine atayacağız.
Biz bu metodu main.c dosyamızda yeniden tanımladığımızda weak özelliği ezilecek ve kesme oluştuğunda bu metod çalışacak. Bu metodu oluşturduktan sonra main fonksiyonu içerisinde NVIC ile interrupt yapılandırmasını oluşturacağız.
void CAN1_RX0_IRQHandler(void)
{
//Kesme oluştuğunda verinin hazır olduğunu anlayabiliriz.
//CAN1 donanımı ile CANBUS hattındaki CAN verisini alıp RxMessage değişkenimize atama yapılıyor
CAN_Receive( CAN1, 0, &RxMessage);
//Aşağıda RxMessage verisini gerekli değişkenlere atıyoruz.
//Gördüğünüz üzere mesajın id değerini ve mesaj içeriğini almış olduk
canbusData_id[0] = RxMessage.StdId;
canbusData_0[0] = RxMessage.Data[0];
canbusData_1[0] = RxMessage.Data[1];
canbusData_2[0] = RxMessage.Data[2];
canbusData_3[0] = RxMessage.Data[3];
canbusData_4[0] = RxMessage.Data[4];
canbusData_5[0] = RxMessage.Data[5];
canbusData_6[0] = RxMessage.Data[6];
canbusData_7[0] = RxMessage.Data[7];
//Artık bu CAN verisini mainprogramda ihtiyaç olan yerlerde kullanabilmekteyiz.
}
Hadi biraz main fonksiyonumuza göz atalım.
İlk olarak kodu parça parça anlatıp en son bütünüyle paylaşacağım.
CAN_InitStructure.CAN_TTCM = DISABLE;
CAN_InitStructure.CAN_ABOM = DISABLE;
CAN_InitStructure.CAN_AWUM = DISABLE;
CAN_InitStructure.CAN_NART = DISABLE;
CAN_InitStructure.CAN_RFLM = DISABLE;
CAN_InitStructure.CAN_TXFP = DISABLE;
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
//42Mhz CAN1 donanımının bağlı olduğu APB1 busın frekansı
// 42 / 2* ( 12 + 8 + 1 ) = 1MBaud
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
CAN_InitStructure.CAN_BS1 = CAN_BS1_12tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_8tq;
CAN_InitStructure.CAN_Prescaler = 2;
CAN_Init(CAN1, &CAN_InitStructure);
Burada bus haberleşme frekansını 1 MBaud olarak belirledik. Burada bus frekansı ile birlikte SJW, BS1, BS2 ve ön bölücü değerleri ile hesaplayarak frekans ayarını yapmış olduk. Datasheet dökümanına göre STM32F407 işlemcisi APB1 bus frekansı 42 MHz değerindedir.
BusFrequency / Prescaler * ( BS1 + BS2 + SJW ) .
Sırada interrupt yapılandırması var.
// FIFO 0 mesaj kesmesini aktifleştirme
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
İlk olarak CANBUS için FIFO özelliğini özelliğini aktifleştirdik. FIFO ile veriler geliş sırasıyla işlenecektir. İlk gelen ilk işlenmiş olacaktır. Yani veri geldikçe kuyruğa eklenecektir.
Buraya kadar anlatılanlar ile CANBUS kullanımını göstermiş olduk
Aşağıda main fonksiyonunun bütün içeriğini görebilirsiniz.
int main()
{
//İlk olarak CAN bus üzerinde APB1'i aktifleştirelim.
RCC_APB1PeriphClockCmd(RCC_APB1Periph_CAN1, ENABLE);
//Ve GPIO Bus'ını aktifleştirelim.
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOD, ENABLE);
//CAN bus haberkelşmesi için gereken pinleri ayarlayalım ve aktifleştirelim.
GPIO_InitTypeDef GPIO_InitStructure;
//GPIO_Mode_AF Olarak seçmemiz önemlidir.
//Bu sayede pinleri input ve output dışında alternatif işler içinde kullanabileceğiz
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;
GPIO_Init(GPIOD, &GPIO_InitStructure);
//Alternate olarak ayarlanan pinleri CAN1 haberleşmesi için ayarlıyoruz
GPIO_PinAFConfig(GPIOD, GPIO_PinSource0, GPIO_AF_CAN1);
GPIO_PinAFConfig(GPIOD, GPIO_PinSource1, GPIO_AF_CAN1);
//Ardından CAN BUS yapılarını oluşturup ayarlamaları yapıyoruz
CAN_InitTypeDef CAN_InitStructure;
CAN_FilterInitTypeDef CAN_FilterInitStructure;
//İlk olarak esetlemek iyidir ve DeInıt yapıyoruz
CAN_DeInit(CAN1);
CAN_StructInit(&CAN_InitStructure);
CAN_InitStructure.CAN_TTCM = DISABLE;
CAN_InitStructure.CAN_ABOM = DISABLE;
CAN_InitStructure.CAN_AWUM = DISABLE;
CAN_InitStructure.CAN_NART = DISABLE;
CAN_InitStructure.CAN_RFLM = DISABLE;
CAN_InitStructure.CAN_TXFP = DISABLE;
CAN_InitStructure.CAN_Mode = CAN_Mode_Normal;
// 42 / 2* ( 12 + 8 + 1 ) = 1MBaud
CAN_InitStructure.CAN_SJW = CAN_SJW_1tq;
CAN_InitStructure.CAN_BS1 = CAN_BS1_12tq;
CAN_InitStructure.CAN_BS2 = CAN_BS2_8tq;
CAN_InitStructure.CAN_Prescaler = 2;
CAN_Init(CAN1, &CAN_InitStructure);
/* CAN filter yapılandırması*/
//Veriye herhangi bir filtre koymadığım için bütün filtre mask değerleri 0 yaptım
CAN_FilterInitStructure.CAN_FilterNumber = 0;
CAN_FilterInitStructure.CAN_FilterMode = CAN_FilterMode_IdMask;
CAN_FilterInitStructure.CAN_FilterScale = CAN_FilterScale_32bit;
CAN_FilterInitStructure.CAN_FilterIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdHigh = 0x0000;
CAN_FilterInitStructure.CAN_FilterMaskIdLow = 0x0000;
CAN_FilterInitStructure.CAN_FilterFIFOAssignment = 0;
CAN_FilterInitStructure.CAN_FilterActivation = ENABLE;
CAN_FilterInit(&CAN_FilterInitStructure);
//FIFO0 CAN1 için aktifleştirdim.
CAN_ITConfig(CAN1, CAN_IT_FMP0, ENABLE);
//Interrupt yapılandırmasını hazırlayalım
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = CAN1_RX0_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
while(1)
{
//Call above method to send a canbus data
Can1WriteData(0x01, 0x12,0x31,0x1A,0x00,0x00,0x00, 0x56 ,0x25 );
//And wait for some time to prevent flooding te bus
while(65000--);
}
}
Bu yazımızda bu kadar Bizi takip etmeyi unutmayın
İyi çalışmalar ve CAN BUS haberleşmeleri dilerim.
Burak Hamdi TUFAN…
Comments