Kamis, 29 September 2016

Serial USART Interrupt pada STM32F4 Discovery

Berdasarkan pengalaman, interupt biasanya sangat dibutuhkan untuk beberapa project. Terutama project yang sifatnya multi tasking. Mangkanya perlu pemahaman tentang interupt yang cukup bagi developer microcontroller. Dan biasanya, seringkali saya memakai USART mode interupt pada semua project microcontroller saya sejak jaman kuliah, karena lebih mudah dan 99.99% data diterima dengan baik(yang penting cable noise free lah). 

Nah, pada tulisan kali ini, saya ingin share sedikit cara penggunaaan interupt ini. Saya akan mencoba menyalakan lampu dan pembacaan ADC dari hyperminal. Ada 4 user IO led di STM32F4 Discovery. Saya ingin mencoba menyalak LED satu persatu, nyala semua, dan mematikan semua LED. Sedangakn untuk ADC, saya akan membaca tegangananalog dari potensio yang ada pada Pin A2. 

Untuk Inisialisasi USART, bisa dilihat dibawah ini:


void init_USART1(uint32_t baudrate){

    /* This is a concept that has to do with the libraries provided by ST
     * to make development easier the have made up something similar to
     * classes, called TypeDefs, which actually just define the common
     * parameters that every peripheral needs to work correctly
     *
     * They make our life easier because we don't have to mess around with
     * the low level stuff of setting bits in the correct registers
     */

    GPIO_InitTypeDef GPIO_InitStruct; // this is for the GPIO pins used as TX and RX
    USART_InitTypeDef USART_InitStruct; // this is for the USART1 initilization
    NVIC_InitTypeDef NVIC_InitStructure; // this is used to configure the NVIC (nested vector interrupt controller)

    /* enable APB2 peripheral clock for USART1
     * note that only USART1 and USART6 are connected to APB2
     * the other USARTs are connected to APB1
     */

    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);

    /* enable the peripheral clock for the pins used by
     * USART1, PB6 for TX and PB7 for RX
     */

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

    /* This sequence sets up the TX and RX pins
     * so they work correctly with the USART1 peripheral
     */

    GPIO_InitStruct.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7; // Pins 6 (TX) and 7 (RX) are used
    GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF;             // the pins are configured as alternate function so the USART peripheral has access to them
    GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;        // this defines the IO speed and has nothing to do with the baudrate!
    GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;            // this defines the output type as push pull mode (as opposed to open drain)
    GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;            // this activates the pullup resistors on the IO pins
    GPIO_Init(GPIOB, &GPIO_InitStruct);                    // now all the values are passed to the GPIO_Init() function which sets the GPIO registers

    /* The RX and TX pins are now connected to their AF
     * so that the USART1 can take over control of the
     * pins
     */

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource6, GPIO_AF_USART1); //
    GPIO_PinAFConfig(GPIOB, GPIO_PinSource7, GPIO_AF_USART1);

    /* Now the USART_InitStruct is used to define the
     * properties of USART1
     */

    USART_InitStruct.USART_BaudRate = baudrate;                // the baudrate is set to the value we passed into this init function
    USART_InitStruct.USART_WordLength = USART_WordLength_8b;// we want the data frame size to be 8 bits (standard)
    USART_InitStruct.USART_StopBits = USART_StopBits_1;        // we want 1 stop bit (standard)
    USART_InitStruct.USART_Parity = USART_Parity_No;        // we don't want a parity bit (standard)
    USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; // we don't want flow control (standard)
    USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx; // we want to enable the transmitter and the receiver
    USART_Init(USART1, &USART_InitStruct);                    // again all the properties are passed to the USART_Init function which takes care of all the bit setting


    /* Here the USART1 receive interrupt is enabled
     * and the interrupt controller is configured
     * to jump to the USART1_IRQHandler() function
     * if the USART1 receive interrupt occurs
     */

    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); // enable the USART1 receive interrupt

    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;         // we want to configure the USART1 interrupts
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;// this sets the priority group of the USART1 interrupts
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;         // this sets the subpriority inside the group
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;             // the USART1 interrupts are globally enabled
    NVIC_Init(&NVIC_InitStructure);                             // the properties are passed to the NVIC_Init function which takes care of the low level stuff

    // finally this enables the complete USART1 peripheral
    USART_Cmd(USART1, ENABLE);
}

 Jika terjadi penerimaan data dari USART , maka akan terjadi interupt dan siklus program akan masuk ke vector interupt serial. Vector interupt dalam codingan ini adalah sebagai berikut:

void USART1_IRQHandler(void){

    // check if the USART1 receive interrupt flag was set
    if( USART_GetITStatus(USART1, USART_IT_RXNE) ){

        static uint8_t cnt = 0; // this counter is used to determine the string length
        char t = USART1->DR; // the character from the USART1 data register is saved in t

        if(t=='q')
        {
            GPIO_SetBits(GPIOD, GPIO_Pin_12);
            GPIO_ResetBits(GPIOD, GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
        }
        else if(t=='w')
        {
            GPIO_SetBits(GPIOD, GPIO_Pin_13);
            GPIO_ResetBits(GPIOD, GPIO_Pin_12|GPIO_Pin_14|GPIO_Pin_15);
        }
        else if(t=='e')
        {
            GPIO_SetBits(GPIOD, GPIO_Pin_14);
            GPIO_ResetBits(GPIOD, GPIO_Pin_13|GPIO_Pin_12|GPIO_Pin_15);
        }
        else if(t=='r')
        {
            GPIO_SetBits(GPIOD, GPIO_Pin_15);
            GPIO_ResetBits(GPIOD, GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_12);
        }
        else if(t=='t')
        {
            GPIO_SetBits(GPIOD, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
        }
        else if(t=='x')
        {
            GPIO_ResetBits(GPIOD, GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15);
        }
        else if(t=='a')
        {
            voltage=(3300*ADC3ConvertedValue)/4095;
            float_print("Voltage =", voltage);
        }

    }
}

Saya akan mengontrol LED dengan karakter sederhana seperti diatas, tanpa protokol yang aneh aneh berhubung hanya menggunakan 1 microcontroller. Yang lumayan menjadi masalah disini adalah fungsi "sprint" dari library stdio tidak bisa untuk mengirimkan data dalam bentuk float. Hal ini sedikit berbeda dengan Library stdio pada IDE untuk atmelAVR yaitu codeVision VAR yang bisa digunakan untuk processing string dari variable float. Untuk mengatasi hal tersebut, saya membuat fungsi sederhana, yang lumayan efektif lah untuk kasus ini, untuk mengirimkan hasil konversi pembacaan tegangan ADC. Berikut fungsi yang saya buat:

void float_print(char* kalimat, int float_value)
{
    char buffer[20], buffer2[50];
    int bigvalue;
    int smallvalue;

    bigvalue=float_value/1000;//ini berisi angka di depan tanda koma
    smallvalue=(float_value%1000)/10;//ini berisi angka dibelakang koma
        sprintf(buffer,("%d.%d"),bigvalue, smallvalue);
    memset(buffer2,0,strlen(buffer2));//clear all memory
    strcat(buffer2,kalimat);//string "kalimat" ditambahkan ke string "buffer2"
    strcat(buffer2,buffer);//string "buffer" ditambahkan ke string "buffer2" 
    strcat(buffer2," volt \n\r");
    USART_puts(USART1, buffer2);
    memset(buffer2,0,strlen(buffer2));//clear all memory
}

Berikut hasil akhir di hyperterminal.



 Video nya juga bisa dilihat di youtube.



Gitu saja lah, untuk main.c nya bisa di download disini ya.