Not: Bu yazıdaki kodlar ve register isimleri (TXDR, CFG1, DSIZE) STM32H7 HAL'ine göredir.
SPI ile çalışırken, IOC dosyasında DataSize parametresine 8-bit, 16-bit veya 32-bit gibi değerler verebiliyorum. Fakat veri gönderirken kullandığımız fonksiyonun signature'ı şöyle:
HAL_StatusTypeDef HAL_SPI_Transmit_IT(SPI_HandleTypeDef *hspi, uint8_t *pData, uint16_t Size)
Burada pData, uint8_t *pData olarak tanımlanmıştır.
Bunu fark edince şu sorular aklıma geldi: DataSize'ı IOC dosyasında neden ayarlıyorum? DataSize tam olarak ne işe yarıyor? Bu durum veri kaybına yol açar mı?
Diyelim ki yapımız şu:
uint16_t TX_Data[] = {1, 2, 3, 4};
HAL_SPI_Transmit_IT(&hspi1, (uint8_t*)TX_Data, 4);
1. uint8_t* cast'i neden veri kaybettirmez?
uint16_t TX_Data[4], memory'de 8 byte'lık ardışık bir alandır. uint16_t* de uint8_t* de aynı address'i gösterir. Pointer type, compiler'a o address'ten kaç byte okunacağını ve ++ ile kaç byte ilerleneceğini söyler.
Yani (uint8_t*)TX_Data cast'i veriyi deÄŸiÅŸtirmez. Sadece compiler'a "bunu byte byte iÅŸle" deriz.
2. HAL pointer'ı tekrar orijinal type'ına cast ediyor
HAL_SPI_Transmit_IT, verdiğimiz uint8_t* pointer'ını hspi->pTxBuffPtr içinde saklar. Transfer başlamadan önce DataSize'a bakarak hangi ISR'ın çalışacağını seçer. 16-bit için seçilen SPI_TxISR_16BIT, pointer'ı tekrar uint16_t* olarak cast eder.
/* Set the function for IT treatment */
if (hspi->Init.DataSize > SPI_DATASIZE_16BIT)
{
hspi->TxISR = SPI_TxISR_32BIT;
}
else if (hspi->Init.DataSize > SPI_DATASIZE_8BIT)
{
hspi->TxISR = SPI_TxISR_16BIT;
}
else
{
hspi->TxISR = SPI_TxISR_8BIT;
}
SPI_TxISR_16BIT içinde işimize yarayan kısım şu:
static void SPI_TxISR_16BIT(SPI_HandleTypeDef *hspi)
{
/* Transmit data in 16 Bit mode */
__IO uint16_t *ptxdr_16bits = (__IO uint16_t *)(&(hspi->Instance->TXDR));
*ptxdr_16bits = *((uint16_t *)hspi->pTxBuffPtr); // uint16_t* cast'i, 2 byte okunur
hspi->pTxBuffPtr += sizeof(uint16_t); // pointer 2 byte ilerler
hspi->TxXferCount--; // Size kaç verildiyse o kadar azalır
/* ... TxXferCount == 0 olunca TXP interrupt kapatılır ... */
}
Yani:
pTxBuffPtrher seferinde 2 byte (sizeof(uint16_t)) ilerler. Böylece bir sonraki transferde dizideki bir sonrakiuint16_telemanı okunur.TxXferCount(bizim verdiğimiz Size) her ISR çağrısında 1 azalır. Yani ISR'ın kaç kez çalışacağını, başka bir deyişle kaç tane 16-bit frame gönderileceğini belirler. Bu değer byte sayısı değildir.
Yani zincir şöyle oluyor:
uint16_t a[],(uint8_t*)acast'i ile fonksiyona parameter olarak veriliyor.- HAL bunu
uint8_t*olarakpTxBuffPtriçinde saklıyor. HAL_SPI_Transmit_ITiçinde DataSize kontrol ediliyor ve ISR seçiliyor.- ISR çalışırken
SPI_TxISR_16BITiçinde tekraruint16_t*cast'i yapılıyor ve o address'ten 2 byte birden okunuyor.
3. Peki DataSize tam olarak ne işe yarıyor?
CubeMX'te seçtiğimiz DataSize, MX_SPI1_Init() içinde şöyle set edilir:
hspi1.Init.DataSize = SPI_DATASIZE_16BIT;
Bu değer, SPI'ın CFG1 register'ındaki DSIZE field'ına yazılır ve üç yerde kullanılır.
3.1. Hardware: Bir frame kaç bit?
Bir frame'de SCK'nın kaç clock pulse üreteceğini belirler. 16-bit seçtiğimizde, CS aktifken 16 clock pulse çıkar ve 16 bit shift edilir.
3.2. Hardware: TXDR access width ve FIFO packing
TXDR'a 8, 16 veya 32-bit access yapılabilir. Access width, FIFO'ya tek seferde kaç bit'lik data girdiğini belirler. Access width frame size'dan küçük olmamalıdır. Büyükse data packing devreye girer: örneğin 16-bit frame'de yapılan 32-bit write, FIFO'ya tek seferde 2 frame koyar. Access width DSIZE ile uyumsuz olursa frame'ler yanlış pack edilir.
3.3. Software: HAL hangi ISR'ı seçecek?
Bölüm 2'deki seçim kodu, DataSize'a bakarak TxISR function pointer'ını set eder. pData'nın uint8_t* olarak gelip ISR içinde uint16_t* olarak cast edilmesinin kararı burada verilir.
4. Peki veri kaybı ne zaman olur?
DataSize, buffer'ın element type'ı ile uyumluysa veri kaybı olmaz. Uyumsuzsa olur. Aynı diziyi DataSize = 8-bit iken gönderdiğimizi düşünelim:
uint16_t TX_Data[] = {1, 2, 3, 4}; // memory'de: 01 00 02 00 03 00 04 00
HAL_SPI_Transmit_IT(&hspi1, (uint8_t*)TX_Data, 4); // DataSize = 8BIT
Size, byte sayısı değil frame sayısıdır. 8-bit modda 4 frame = 4 byte gider: 01 00 02 00. Yani dizinin sadece ilk iki elemanı gönderilir; 3 ve 4 hiç gitmez.
Kısacası (uint8_t*) cast'i tek başına veri kaybettirmez. Veri kaybına, DataSize'ın buffer type'ı ile uyumsuz olması yol açar.




