Programmer-To-Go özelliği pickit2 programlayıcısı üzerindeki spi flash a hex dosyasını yazmaya, daha sonrada bilgisayara ihtiyaç olmadan sadece programlayıcıyı powerbank vb bir kaynak ile besleyerek, programlayıcı üzerindeki buton ile diğer mcu ları programlamak için kullanılan bir özellik. Varlığından yıllardır haberdarım ama şimdiye kadar aktif olarak hiç kullanmadım, ofis ortamında pc yazılımını kullanmak daha kolay geliyor ![]() Pickit2 yazılımında zaten bu özellik var, programlayıcınızda spi flash ve buton bağlantısı varsa extra birşey yapmanıza gerek yok. Siz tam olarak ne yapmak istiyorsunuz, iftar öncesi anlama kabiliyetim biraz azalmış olabilir ![]() |
ICSP üzerinden SPI flash programlamaktan bahsetmiyorum. Pickit2 cihazında varsayılan olarak I2C EEPROM'lar var. SPI flash PC tarafındaki yazılımda destekleniyor. Ancak pickit2 firmware yazılımında sadece I2C hafızalara destek var. I2C hafızalar SPI flash hafızalara göre çok pahalılar. Microchip masaüstü yazılımına SPI flash desteği koymuş ama Pickit2 içindeki firmware yazılımına bu desteği eklememiş. Ben programmer to go özelliğini SPI flash hafıza ile kullanmak için firmware üzerinde çalışıyorum. |
Aynı şeyden bahsediyoruz üstadım. Bende orjinal pickit2 ve pickit3 serileri var içinde yanlış hatırlamıyorsam 2 adet 24c512 eeprom var. Bunlar I2C ile çalışıyor, sizin amacınızı şimdi anladım. I2C yerine SPI arabirimli chip kullanmak istiyorsunuz. 64 byte sınırı sanırım usb-hid transfer limiti ile ilgili. Çünkü Usb-hid üzerinden bir veri paketi max 64 byte olabilir. İftardan sonra sistemler normale dönünce paylaştığınız dosyayı incelemeye çalışırım. ![]() |
64B sınırı bir problem değil. Kodu da buraya atayım.
|
Kodda bir geliştirme daha yaptım. AddrExtEE() fonksiyonunu değer döndürür hale getirdim. Fonksiyona istenen byte gönderildiğinde ext_ee_addr'den ilgili byte'ı hesaplayıp geriye döndürecek. low byte = 0; medium byte = 1; high byte = 2; olarak parametre belirledim. Okuma ve yazma fonksiyonları içinde üç farklı 8 bitlik değişken atadım. add_l; add_m; add_h; Okuma/yazma yapılmadan önce adres bilgisi alınarak yukarıdaki değişkenlere atılacak, adres bilgisi byte'lara bölünmüş şekilde önceden hazır olacak.Kesintisiz bir şekilde adres ve veri bilgisi arada duraksama olmadan iletilecek. Bu sayede veri akışının adres hesaplaması nedeniyle kesintiye uğramasını da engellemiş olurum. Bu sayede adres ve veriyi önceden hazır halde tutarak iletişim hızını da artırabilirim. Orjinal kodda low adres hesaplanıp gönderiliyor ardından high adres hesaplanıp gönderiliyordu. I2C iletişimi zeten yavaş olduğundan arada adres hesaplaması için akışı bölmek sorun yaratmıyordu. I2C iletişiminde EEPROM çiplerinin kendi adreslerini de bus üzerinden göndermek gerektiği için AddrExtEE fonksiyonunda karmaşık bir kod çalışıyordu. SPI veriyolunda Çip seçimi CS pini ile yapıldığı için bus üzerinde I2C'deki gibi cihaz adresi gönderme olayı olmadığından, AddrExtEE fonksiyonumuzun sadece okuma/yazma yapılacak adresleri döndürmesi yeterlidir.
|
Spi desteği olan hangi EE chipi kullanmayı planlıyorsunuz? Genelde Spi flash larda içine veri yazmadan önce ilgili hücrenin 0xFF (bu projede tüm chipin içeriğinin) olması gerekir. Spi flaslarda silme işlemi, allchip, sector ve block bazlı yapılır. I2C lerde direk üzerine veri yazılabiliyor bu durumu göz önünde bulundurun. Seçtiğiniz spi chip üzerine veri yazmayı destekliyorsa yukarıdaki uyarıyı göz ardı edebilirsiniz. Siz konuya çok daha fazla kafa yordunuz ama kodunuzdaki aşağıdaki kısım biraz garip geldi. Adres parçalama işlemine bir mana veremedim. Farklı bir mantığı varsa açıklarsanız iyi olur. unsigned int ext_ee_addr; C18 derleyicide unsigned int 16 bitlik yani 2 byte yer kaplar. Düşük, orta, yüksek byte seçimlerinde 8 bit kaydırmanız gerekmez mi? Aynı zamanda ext_ee_addr tipinin 3 byte veya daha uzun bir değişken olarak tanımlanması gerekmez mi? Ör: unsigned shot long (24bit) veya unsigned long(32bit) gibi. //EE adresinin düşük byte'ını oluştur. Generate low byte of EE addres if(address == 0) { add = (ext_ee_addr << 6) & 0xFF; //<<--- düşük için niye sola 6 bit add = ext_ee_addr & 0xFF; // normalde bu şekilde düşük byte alınır } //EE adresinin orta byte'ını oluştur. Generate middle byte of EE addres if(address == 1) { add = (ext_ee_addr >> 2) & 0xFF; // <<--- orta için niye 2 bit sağa add = (ext_ee_addr>>8) & 0xFF; // normalde bu şekilde yüksek byte alınır. } //EE adresinin yüksek byte'ını oluştur. Generate high byte of EE addres if(address == 2) { add = (ext_ee_addr >> 10) & 0xFF; // <-- yüksek için tekrar sağa 10 bit kaydırıldı // ext_ee_addr 2 byte olduğu için 3. kez sağa kaydırma yapmanın bir anlamı yok } return add; |
ext_ee_addr değişkeni 64'ün katlarıdır. Açıklama kısmında multiples of 64 diye yazılmış. ext_ee_addr 0 = byte 0 - 63, ext_ee_addr 1 = byte 64 - 127 ext_ee_addr 2 = byte 128 - 191 ... ext_ee_addr 65535 byte 4.194.240 - 4.194.304 SPI flash kullanacağım. SPI flashlar geleneksel 25xx SPI EEPROM'lardan farklı olarak 24bit adresleme ile çalışır. Dolayısı ile adres verisini 24 bit olarak göndermek gerekir. Programmer to Go'da sadece PC'den PICkit'e veri kaydederken EEPROM'a veri yazılır. Dolayısı ile çipin önceden silinmiş olup olmadığını bir değişken ve if koşulu ile kontrol edip sadece yazma fonksiyonu ilk çalıştığında silme işlemini yapmak bana daha mantıklı geldi. Yazma fonksiyonu ilk çalışmada çipi silecek ve çipi silinmiş olarak işaretleyecek. if(!cleaned) ifadesi cleaned ==0 ise silme rutinini çalıştır: if'ten çıktıktan hemen sonra cleaned ifadesi 1 olarak değiştirilecek. Yazma fonksiyonu tekrar çağrıldığında cleaned'e bakacak. 1 gördüğünde silme işlemini atlayıp doğrudan yazmaya geçecek. Cleaned değişkenini fonksiyonun dışında tanımladım, yazma fonksiyonundan çıktıktan sonra hafızadan silinmeyecek. ext_ee_addr'ı 64 ile çarpar byte adresi haline getiririz. 64, 2 üzeri 6'dır. Yani 6 bit sola kaydırma yaparız. "ext_ee_addr << 6" Low byte için doğrudan 6 bit sola kaydırıp ilk byte'ı maskelemek yeterli. "(ext_ee_addr << 6) & 0xFF" Medium byte için 6 bit sola kaydırdığımız değeri 8 bit sağa kaydırıp medium byte'ın en başa gelmesini sağlarız. Ardından FF ile maskeleyerek alırız. İşte 6 bit sola 8 bit sağa kaydırınca sonuç olarak 2 bit sağa kaydırmış oluyoruz. "((ext_ee_addr << 6) >> 8) & 0xFF" High byte için 6 bit sola kaydırdığımız değeri iki sefer 8 bit sağa kaydırıp high byte'ın en başa gelmesini sağlarız. Ardından FF ile maskeleyerek alırız. İşte 6 bit sola 16 bit sağa kaydırınca sonuç olarak 10 bit sağa kaydırmış oluyoruz. "(((ext_ee_addr << 6) >> 8) >> 8) & 0xFF" |
Anlaşılan gerçek adresi bulmak için 64 ile çarpma işlemi için böyle bir yapı kurulmuş. Benim önerim 18f2550 de donanımsal çarpma desteği var. Bundan faydalanıp adres = ext_ee_addr * 64; // Eski mesaj yanlış:tek bir clock cycle Düzeltme doğrusu:28 clock cycle (değişken tipi uint16 olduğu için) de bu işlem gerçekleşir. Şeklinde bir kullanım işleri daha kolaylaştırır. Diğer bir öneri ise: Her 64 byte lik paket yazmada ext_ee_addr++; bir artırılıyor. Bunun yerine ext_ee_addr +=64; Şeklinde yaparsanız sonraki gerçek adres zaten elinizin altında olur. Çarpma veya bit maniplasyonu gibi dertlerden kurtulursunuz. Bu sayede yukarıdaki mesajda belirttiğim gibi her seferininde sağa 8 in katları bit kaydırarak adresin byte verilerine erişebilirsiniz. Bit kaydırma yerine pointer kullanımınıda düşünebilirsiniz. Bu proje kullanımında performans konusunda ve kod okunurluğu konusunda bir farkı olmaz ama, genel alışkanlık ve kullanım tercihi olarak şöylede yapabilirsiniz. uint32 adres; uint8 *p8; //pointer tanımlama uint8 adr; p8 = (uint8*)&adres; // pointer adresin lsb byte i işaret ediyor. adr = *p8; // lsb ilk byte p8++; // pointeri bir artır adr = *p8; // 2. byte p8++; // sonraki byte adr = *p8; // 3. byte p8++; // sonraki byte adr = *p8; // 4. byte |
Ama hafıza işte, bunu yapmak için 16 bitlik ext_ee_addr değişkenini 32 bite çıkarmam lazım. ext_ee_addr değişkeni global, hafızada sürekli yer işgal ediyor. Zaten hafıza öyle bir ayarlanmış ki bir byte bile fazladan kullandın mı hafıza sınırı aşıldı diyen link step hatasına düşüyor. Dolayısı ile hafızayı idareli kullanmam lazım. ![]() Ama AddExtEE içerisine yerel değişken atayıp ext_ee_addr*64'ü buraya atmak düşünülebilir. |
Hafıza (RAM) kapasitesi ile ilgili sorun / yetmeme durumu varsa pointer ile aynı hafıza bölgesini ortak kullanabilirsiniz. Bu sayede bit maniplasyonu ve kaydırma gibi işler için local değişken tanımlamanıza bile gerek kalmaz. Yukarıdaki örnekde uint8 adr; tanımlamasını sizin kod uyumunuza benzemesi ve daha rahat anlatmak içindi. Yoksa *p8 zaten ilgili hafıza (ram) adresini bir byte olarak işaret ediyor. Doğrudan *p8 şeklindede kullanılabilir. MPlab ide sini kullanmadığım için deneme yapma imkanım yok ama ramde 2 byte alacak kadar yer vardır herhalde 2048 byte ram çokda az değil ![]() |
Şu pointer olayına bir bakayım. Teşekkürler. İnterneti kurcalarken bu sayfayı buldum. Çok işime yarayacak bilgiler var. Hem de Mplab (x olmayan düz mplab) üzerinden anlatılmış. https://www.electronicwings.com/pic/pic18f4550-spi Güncel derleyiciler bu tip çevresel okuma yazma işlemleri için dahili fonksiyonlara sahip. SPI_write() diyorsun gönderiyor, SPI_read() oku diyorsun alıyor. Bayrağıyla, tampon taştı taşmadı vs. ile uğraşmıyorsun. Bu sayede gereksiz teferruatlarla uğraşmadan asıl işine odaklanabiliyorsun. Pickit2 kodlarının yazılmış olduğu Mplab ide derleyicisi bunlardan yoksun. Mecbur her şeyi elle yapıyoruz. Mesela Pickit3'ün yazılmış olduğu mplab X SPI ve İ2C için dahili fonksiyonlara sahip. Bu nedenle kodları daha sade. |
Pickit3 de U3 ve U4 hafıza chipleri U3=i2c U4=spi modunda çalışıyor. Belki fikir verebilir. Benim emektarlar, Pickit2 daha fazla kullanılmış ![]() < Resime gitmek için tıklayın > |
Hiç PICkit3'üm olmadı. 😢 PICkit3 kodlarına da bakmıştım. Mplab X ile yazmışlar. Hafıza iletişimi için MplabX'in dahili SPİ fonksiyonlarını kullanmışlar. Benim işime yarayabilecek düşük seviyeli bir SPI haberleşme kodu mevcut değil.
PIC18'lerde olup, PIC16'larda olmayan; namını çok duyduğum 8x8 Hardware multiplier diye bir özellik var. Ondan mı bahsediyorsunuz? |
Çok birşey kaybetmiş değilsiniz aslında. Alışkanlıkdanmıdır yazılımının kolay ve hızlı kullanılmasındanmıdır pickit2 yi daha çok tercih ediyorum. Diğeri pickit2 nin desteklemediği (device list dosyasını düzenleyerek eklemek bir yöntem ama her zaman için optimum yöntem olmuyor) bazı mcular olduğunda arada bir kullanıyorum. Evet hardware multiplier den bahsediyoruz ama yukarıdaki mesajda hatalı ifade ettim (o mesajı yazarken 32bit arm mimarisi gibi düşündüm bir an için). 1 cycle de çarpma işlemini 8 bitlik işaretsiz sayılarda yapabiliyor. Aşağıdaki tablo çarpma işleminin donanımsal olup olmamasının ROM ve cpu zamanı kullanımı açısından karşılaştırmasını veriyor. Tablo 18f2550 nin pdf inden alındı. Tablonun ilk satırı için unsigned 8 bit işlemlerde 26byte (13 word) daha az ROM kullanıyor ve 69 kat daha hızlı işlemi sonuçlandırıyor. Diğer veri tipleri için tabloya aynı mantıkla bakabilirsiniz. < Resime gitmek için tıklayın > |
Ama medium ve high byte'ların hesabında sağa bit kaydırma yapılıyor. Bunlar matematiksel olarak 2^8 ve 2^16'ya bölme işlemine tekabül ediyor. Anladığım kadarıyla hardware multiplier sadece integer'ları çarparken işe yarıyor. Bölme işleminde kullanamıyoruz. |
Mcu nun komut setinde sola veya sağa bit kaydırma (Rotate Left ve Rotate Right) komutları var. Fakat bu mcu 8 bitlik olduğu için, buradaki 1 cpu zamanı 8 bitlik değişkenler üzerinde işlem yapıldığında geçerli. Sizin örnekdeki eeprom adresini tuttuğunuz değişken gibi 16 bitlik veya 32 bitlik bir değişkenin bitlerini sağa veya sola kaydırmak istediğinizde bu daha fazla cpu zamanına mal olacaktır çünkü aynı anda sadece 8 bit üzerinde işlem yapma kapasitesine sahip. Bu durumda adres değişkeni içerisindeki byte lara erişmek için pointer kullanmak daha hızlı bir yöntem olacaktır. 8 e veya 16 ya bölme işlemlerinde derleyicinin Rotate komut setlerini kullanması beklenir. (Ondalık / floating point çarpma ve bölme işlemleri farklı bir konu) Birde hız herşey demek değildir, yerine göre bir işlemin 10us de bitmesi ile 80us de bitmesi çokda önemli değildir, fakat aradaki farkları bilmek ve gerekli olduğu zamanlarda doğru yöntemi tercih edebilmek önemli / avantajlı hale gelecektir. < Resime gitmek için tıklayın > Not: Doğrudan assembler yazmıyorsanız, araya kullandığınız compiler ve onun ayarları, optimizasyon yetenekleri devreye girecektir. Çıkan makine kodu ve performansı değişecektir. Assemblerde yazdığınız kodlar gördüğünüz şekilde işlenecektir. İyi yazdıysanız iyi, kötü yazdıysanız kötü performansda çalışacaktır. Derleyici farkına örnek olaması açısından, geçmişte elimdeki mcunun bir pininden aşağıdaki gibi basit bir kod ile kaç Hz sinyal alabilirim diye test yapmıştım. pseudo code while(1) { pin1 = H; pin1 = L; } Aynı kod aynı mcu, aynı configrasyonda çalıştırıldığında Derleyici 1 : 1.2 Mhz Derleyici 2 : 6.0 Mhz C ile kodlama yapıyorsanız kullandığınız derleyicinin kabiliyetlerine ve optimizasyon seçimlerine göz atmanızda fayda var. |
Mesajınıza daha sonra ilave ettiğiniz kısmı sonradan gördüm. SPI modülünü aktif ettiğinizde zaten onun bağlı olduğu IO larda uygun şekilde konfigüre edilir. TRIS yapmaya gerek yok. TRIS kullanmanın gereksiz cpu zamanı ve romda yer kaplaması haricinde bir zararıda yok :) Edit2: C derleyicilerde bu işlem arka planda yapılır, yukarıdaki açıklama buna göre yapıldı ama assembler kullanıyorsanız pinleri uygun şekilde input veya output olarak ayarlamanız gerekir. Edit: Mesajı sonradan düzenlediğiniz için yukarıdaki cevap askıda kaldı. |
Programmer to Go'nun EEPROm arayüzü şu şekilde çalışıyor. Firmware kodlarını inceleyerek çalışma mantığını ortaya çıkardım.
void PK2GoInit fonksiyonu EEPROM'u hazır hale getiriyor. Gerekli ön hazırlıkları yapıyor.
void Wr64ToExtEE fonksiyonu her seferinde 64B'lık veri bloklarını EEPROM'a yazıyor.
void Rd64FromExtEE fonksiyonu her seferinde 64B'lık veri bloklarını EEPROM'dan okuyor.
void AddrExtEE fonksiyonu okunacak/yazılacak adres bilgisini EEPROM'a gönderiyor.
Bu üç fonksiyon doğrudan EEPROM ile iletişim sağlıyor. Geri kalan fonksiyonların EEPROM ile doğrudan bir haberleşmesi yok.
Okuma yazma işlemleri genel olarak şöyle işliyor. PC yazılımının gönderdiği veriyi void WrByteExtEE(unsigned char byteval) fonksiyonu 64Byte'lık bir tampon alanda biriktiriyor. Tampon dolduğunda Wr64ToExtEE fonksiyonu çağırarak biriken verinin EEPROM'a yazılması sağlıyor.
EEPROM'dan okuma yapılacağı zaman void Rd64FromExtEE fonksiyonu ile okunmuş olan 64B'lık veri bloğu unsigned char RdByteExtEE(void) çağrılarak okunuyor. Tampon alan boş ise void Rd64FromExtEE çağrılarak EEPROM'dan veri okuması yapılıyor.
Cleaned diye char tipinde bir değişken atadım.
Cleaned = 0, çip kirli; ;D
Cleaned = 1, çip temiz, veri yazılabilir.
Değişkenin başlangıçtaki varsayılan değeri 0 olacak. Wr64ToExtEE fonksiyonu çalıştığında ilk başta bunu kontrol edecek. Çip kirli ise önce bir temizleyecek. Çip temiz ise doğrudan yazmaya geçecek. Bu sayede her seferinde çipi silmesini engelleyeceğim.
Bir de siz kontrol edebilir misiniz? Kusuru eksiği falan var mı?
pk_prog2go.c dosyası
https://disk.yandex.com.tr/showcaptcha?cc=1&mt=179A0246E16A97CAFA2204B012E5C956DB403561153364F4EA3923C371D481DA60D35171D5B79BDFFBF8F4BD3E2132D3EACD3AD8C0168D5E666F7C7BBFC2C921D049FDCB5B79949CD001253FC9A4BBD767509D5926A85D3A2B4505E80DBAC7F2FA39929865C53FA1CF59371F9912C873903EB4D8B42C2BC86AE9C1037F98C67C791C0212F284A27DAC12FA68FA41454B95920C6694CA195F8007E7877B1BA7F72A44BAE882DF05B8CC21F33A893ADB23AC9F74306C87638A77B933546E5AAFB5CA2C114348305571E5DFA7D04011EF7E28E90B6D3C6981C2DBA3944188C1&retpath=aHR0cHM6Ly9kaXNrLnlhbmRleC5jb20udHIvZC9ua085dmsta0xyMGt0dz8%2C_1fe43b5bbb264a0734646dc63c9f294f&t=2/1710591619/978481396ef88e0e261839291a3a14c5&u=486806fc-ac832aa9-12b92f72-52b67bcf&s=165e6866275ec6c72b777ac3e6506c39
DH forumlarında vakit geçirmekten keyif alıyor gibisin ancak giriş yapmadığını görüyoruz.
Üye Ol Şimdi DeğilÜye olduğunda özel mesaj gönderebilir, beğendiğin konuları favorilerine ekleyip takibe alabilir ve daha önce gezdiğin konulara hızlıca erişebilirsin.
< Bu ileti mini sürüm kullanılarak atıldı >