Пятница, Сентябрь 18, 2020

Первое включение модуля ECAN

//И так первые шаги по пониманию работы модуля CAN.Работаем с нулевым Буфером в Кольцевом режиме.
//Передаем первый символ введенный с клавиуты компа в проге PuTTY
#include<p18cxxx.h>
#include<delays.h>
#include<xlcd.h>
#include<usart.h>
#pragma config OSC=XT
#pragma config WDT=OFF
#pragma config LVP=OFF
#pragma config MCLRE=OFF
#pragma config BOREN=OFF
#pragma config PBADEN=OFF

#define DELAY 100
//*******************Переменные**************************************
#pragma udata
char str1[]="Cfg mod Ok";
char str2[]="END Cfg mod Ok";
char checTx[]="buf is empty";
char count=0;
char addr_1st=0;
char i=0;
char j=0;
char RxIn=0;
char RxBuf[17]={0x89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
char asc[17]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
char no_recive[]="not resive";
char CanRxBuf[16]={0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
//********************Прерывания**************************************
#pragma code
void MyHighInt(void);          //Прототипы подпрограммы обслуживания
void MyLowInt(void);           //прерываний

#pragma interrupt MyHighInt    //Это высокоприоритетное прерывание
#pragma code high_vector=0x08  //Это вектор по адресу 0х08
void high_vector(void){
    _asm GOTO MyHighInt _endasm
}

#pragma interrupt MyLowInt     //Это низкоприоритетное прерывание
#pragma code low_vector=0x18   //Это вектор по адресу 0х18
void low_vector(void){
    _asm GOTO MyLowInt _endasm
}
//*******************************************************************
#pragma code
//Функции задержки для LCD
void DelayFor18TCY(void)
{
    Delay10TCYx(2);
}
void DelayPORXLCD(void)
{
    Delay1KTCYx(15);
}
void DelayXLCD(void)
{
    Delay1KTCYx(5);
}
void  ClrLCD(char addr);
void  StrToLCD(char* str,char addr,char delay);
char* HexToASCII(char *sour,char *dest,int len);
void  InitCAN(void);
void  TxCan(char TxByte);
void  RxCan(void);
//******************Главная функция***************************************
void main(void)
{
    ADCON1=0;
    ADCON1=0x0f;
    //If using interrupts, ensure that the GIE and PEIE
    //bits in the INTCON register (INTCON<7:6>) are set.
    RCONbits.IPEN=0;          //включено только высокоприоритетное прерывание
    INTCONbits.GIE=1;         //глобальное разрешение прерывания
    INTCONbits.PEIE=1;        //When IPEN = 0:1 = Enables all unmasked peripheral interrupts
    //Конфигурируем USART
    OpenUSART( USART_TX_INT_OFF &USART_RX_INT_ON &USART_ASYNCH_MODE &USART_EIGHT_BIT &USART_CONT_RX &USART_BRGH_HIGH,25 );
    //Конфигурируем LCD
    OpenXLCD(FOUR_BIT & LINES_5X7);
    while(BusyXLCD());
    WriteCmdXLCD(CURSOR_OFF&BLINK_OFF);
    addr_1st=0x80;            //Адрес первого символа
//*************************CAN********************************************
    InitCAN();
//**Главный цикл программы************************************************
    while(1)
    {
        TxCan(RxBuf[0]);               //отправим сообщение
        RxCan();                       //примем его же
    }
}

//Передача данных
void  TxCan(char TxByte){
//Для передачи сообщений CAN будем использовать только один передающий буфер TXB0.
//Для передачи сообщения сначала необходимо определить, свободен ли передающий буфер (бит
//TXREQ регистра TXB0CON сброшен в 0).
    if(TXB0CONbits.TXREQ)
        return;
//Перед тем как передать сообщение, нужно занести его в регистры передающего буфера следующим образом:
//1) Занести идентификатор сообщения в следующие регистры:
//TXB0SIDH – старший байт стандартной части идентификатора.
//TXB0SIDL – содержит младшие три бита стандартной части идентификатора, бит типа сообщения
//(так как у нас расширенное сообщение, устанавливаем этот бит в единицу), а также два старших бита
//расширенного идентификатора.
//TXB0EIDH – старший байт расширенной части идентификатора.
//TXB0EIDL – младший байт расширенной части идентификатора.
    TXB0SIDH=0xab;                       //пока для проверки заносим что угодно
    TXB0SIDL=0b00000000;
    TXB0EIDH=0x00;
    TXB0EIDL=0x00;
//2) Занести данные в следующие регистры:
//TXB0Dm – m байтов данных (от 0 до 8 байтов).
    TXB0D0=TxByte;
//3) Занести длину данных (биты DLC<3:0>) и сбросить в ноль бит удалённого запроса (TXRTR) (так
//как удалённый запрос не используется) в следующие регистры:
//TXB0DLC – длина данных (количество байтов данных) и бит удалённого запроса.
    TXB0DLC=0b00000001;
//4) Если необходимо, установить приоритет передачи в битах TXPRI <1:0> регистра TXB0CON.
//Доступны следующие значения приоритетов:
//'11' – уровень приоритета 3 (наивысший приоритет)
//'10' – уровень приоритета 2 (высокий приоритет)
//'01' – уровень приоритета 1 (низкий приоритет)
//'00' – уровень приоритета 0 (низший приоритет)
//Чем выше приоритет сообщения, тем раньше оно будет передано. Это имеет смысл при
//использовании нескольких передающих буферов.
    TXB0CONbits.TXPRI0=1;
    TXB0CONbits.TXPRI1=1;
//После заполнения всех регистров передающего буфера передача инициируется установкой в единицу
//бита TXREQ регистра TXB0CON. Этот бит не инициирует саму передачу, а лишь сигнализирует
//модулю, что сообщение в буфере готово к передаче. Как только сообщение будет передано,
//соответствующий буфер генерирует прерывание, сигнализируя о возможности загрузки нового пакета данных.
//В случае необходимости подготовленные сообщения можно сбросить установкой бита ABAT в
//регистре CANCON (сбросить все передающие буферы) или сбросом бита TXREQ в регистре TXB0CON
//(сбросить только один передающий буфер, в данном случае буфер с номером 0). Если сообщение в этот
//момент уже передаётся, сброс не будет иметь эффекта, и сообщение будет передано полностью.
    TXB0CONbits.TXREQ=1;
}

//Приём данных
void  RxCan(void){
    int i;                            
    if(!RXB0CONbits.RXFUL){               //проверим есть что нить в буфере RXB0
        StrToLCD(no_recive,0xc0,DELAY);   //если нет,то выведем на экран
        for(i=0;i<16;i++)                 //почистим буфер
            CanRxBuf[i]=0;
        return;
    }
//Для приёма сообщений будем использовать только один буфер, RXB0.
//Из буфера сборки сообщения MAB готовое сообщение поступает в приёмный буфер и генерируется
//соответствующее прерывание. Чтобы прочитать сообщение из приёмного буфера, нужно выполнить
//следующие действия:
//1) Извлечь идентификатор сообщения из следующих регистров:
//RXB0SIDH – содержит старший байт стандартной части идентификатора.
    CanRxBuf[0]=RXB0SIDH;
//RXB0SIDL – содержит младшие три бита стандартной части идентификатора, флаг удалённого
//запроса для стандартных сообщений (в расширенном сообщении он всегда равен нулю – см. описание
//регистров CAN), бит типа сообщения (так как принимаем только расширенные сообщения, этот бит
//будет установлен в единицу), а также два старших бита расширенного идентификатора.
    CanRxBuf[1]=RXB0SIDL;
//RXB0EIDH – содержит старший байт расширенной части идентификатора.
    CanRxBuf[2]=RXB0EIDH;
//RXB0EIDL – содержит младший байт расширенной части идентификатора.
    CanRxBuf[3]=RXB0EIDL;
//2) Из регистра RXB0DLC считать длину данных.
    CanRxBuf[4]=RXB0DLC;
//3) Извлечь данные из следующих регистров:
//RXB0Dm – m байтов данных (от 0 до 8 байт).
    CanRxBuf[5]=RXB0D0;
//преобразуем и на LCD
    HexToASCII(CanRxBuf,asc,6);
    StrToLCD(asc,0xc0,DELAY);
    
//4) сбросить бит RXFUL в регистре RXB0CON, освобождая буфер для приёма нового сообщения.
    RXB0CONbits.RXFUL=0;
}
//**************************************************************************************************
//Включение и настройка модуля CAN
//Для включения модуля CAN в работу необходимо выполнить следующие действия:
void  InitCAN(void){
//1) настроить выводы RB2 и RB3 порта PORTB как порты ввода-вывода модуля CAN установкой бита
//TRISB<3> в единицу (линия RX), а бита TRISB<2> в ноль (линия TX).
    TRISBbits.TRISB3=1;
    TRISBbits.TRISB2=0;
//2) в регистре CANCON установить биты REQOP2:REQOP0 в '100' (режим настройки), затем ждать,
//пока не установится этот режим, проверяя биты OPMODE2:OPMODE0 в регистре CANSTAT. Как
//только эти биты примут значение '100', значит, режим установился и можно перейти к следующему шагу.
    CANCON=0x80;
    while ((CANSTAT & 0xE0) != 0x80);
    //StrToLCD(str1,0xc0,DELAY);
//3) произвести настройку приёмных фильтров и масок, настройку прерываний (если они
//используются)*. Так как фильтры использоваться не будут, устанавливаем в ноль значения всех
//регистров масок:
//RXMnSIDH – регистры масок, содержащие критерий для старших битов стандартного
//идентификатора.
//RXMnSIDL - регистры масок, содержащие критерий для младших битов стандартного
//идентификатора и старших битов расширенного идентификатора, а также флага
//стандартный/расширенный идентификатор (флаг типа сообщения).
//RXMnEIDH - регистры масок, содержащие критерий для средних битов расширенного
//идентификатора.
//RXMnEIDL - регистры масок, содержащие критерий для младших битов расширенного
//идентификатора.
    RXM0SIDH=0x00;
    RXM0SIDL=0x00;
    RXM1SIDH=0x00;
    RXM1SIDL=0x00;
    RXM0EIDH=0x00;
    RXM0EIDL=0x00;
    RXM1EIDH=0x00;
    RXM1EIDL=0x00;
//4) произвести настройку приёмных буферов. Так как в нашем случае используется только один
//приёмный буфер, нужно настроить его для работы в режиме приёма всех действительных расширенных
//сообщений установкой в '10' битов RXM1:RXM0 регистра RXB0CON и запретить дублирование буфера
//сбросом бита RX0DBEN регистра RXB0CON.
//    RXB0CONbits.RXM0=0;
//    RXB0CONbits.RXM1=1;
//    RXB0CONbits.RXB0DBEN=0;
//Кроме режимов работы модуля CAN дополнительно можно задать режимы приёма сообщений. Они
//задаются с помощью битов RXM1:RXM0 управляющих регистров приёмных буферов (RXBnCON).
//Возможны следующие режимы:
//1) приём всех безошибочных сообщений (RXM1:RXM0 = '00') – принимаются все сообщения, не
//содержащие ошибок, независимо от типа сообщения.
//2) приём безошибочных сообщений только со стандартным идентификатором (RXM1:RXM0 ='01').
//3) приём безошибочных сообщений только с расширенным идентификатором (RXM1:RXM0 = '10').
//4) приём сообщений с ошибками* (RXM1:RXM0 = '11') – в этом случае в приёмный буфер
//копируются все сообщения, в том числе и сообщения с ошибками, независимо от типа идентификатора.
//Этот режим следует использовать только при тестировании системы.
//Прикол в том ,что если включить в CANCON нормальный режим работы (REQOP2:REQOP0 = '000')
//то при таких настройках(RXM1:RXM0 = '11'),MPC2551 будет принимать по TXD и отправлять
//на PIC18F2480 данные по RXD.Если поднять первую ногу TXD MCP2551,то передача байтов не проходит.
//Если замыкать пинцетом отпаянную ногу TXD,то данные проходят.
    RXB0CONbits.RXM0=1;
    RXB0CONbits.RXM1=1;
    RXB0CONbits.RXB0DBEN=0;
//5) произвести настройку скорости передачи. Для этого необходимо рассчитать значения сегментов
//времени и ширины фазового перехода и занести в регистры BRGCON1, BRGCON2, BGRCON3. Более
//подробно процесс настройки скорости передачи описывается ниже.

    BRGCON1=0x00;
    BRGCON2=0B10011001;
    BRGCON3=0b00000010;

//6) настроить регистр CIOCON – установить бит ENDRHI, чтобы притянуть вывод TX к питанию в
//рецессивном состоянии, и сбросить бит CANCAP, чтобы отключить режим захвата CAN с помощью
//модуля CCP1 (этот режим используется для реализации временно?й метки, которую в нашем простом
//примере использовать не будем).    
    CIOCONbits.ENDRHI=1;
    CIOCONbits.CANCAP=0;
//7) установить нормальный режим работы (REQOP2:REQOP0 = '000') и дождаться, пока этот режим
//установится (OPMODE2:OPMODE0 = '000').
//    CANCON=0b00000000;
//    while ((CANSTAT & 0b11100000) != 0b00000000);
//    StrToLCD(str2,0xc0,DELAY);
//4) режим петли (loopback) (REQOP2:REQOP0 = '010') - позволяет осуществить передачу сообщения
//по внутренним цепям модуля от передающего буфера в приёмный буфер без выдачи сообщения на
//шину CAN. Этот режим можно использовать при разработке и тестировании системы. В этом режиме
//бит ACK игнорируется, и устройство будет признавать входящие сообщения от самого себя, как если
//бы они поступали от других узлов. При этом сообщения с шины не принимаются и на шину не
//передаются, включая флаги ошибок и сигналы подтверждения.    
    CANCON=0b01000000;
    while ((CANSTAT & 0b11100000) != 0b01000000);
    //StrToLCD(str2,0xc0,DELAY);
//Далее модуль функционирует в нормальном режиме, то есть можно принимать и передавать
//сообщения.    
}
//*******************Попадаем сюда по прерыванию на Rx**************
void MyHighInt(void){
    int i;
    if(PIR1bits.RCIF){                     //прерывание по Rx?
        RxIn=getcUSART();                  //приняли байт в RxIN
        RxBuf[addr_1st-0x80]=RxIn;
        if(addr_1st==0x90){                //проверим не кончилась ли строка
            while(BusyXLCD());
            SetDDRamAddr(0x80);            //чистим строку
            for(i=0;i<16;i++){
               while(BusyXLCD());
               WriteDataXLCD(' ');
            }
            addr_1st=0x80;                 //начало первой строки
        }
        if(RxIn==0x7F){//(RxIn==0x08){     //если Backspase
            if(addr_1st!=0x80){            //если еще не ввели символ в 0х80
                while(BusyXLCD());           
                SetDDRamAddr(--addr_1st);
                while(BusyXLCD());
                WriteDataXLCD(' ');
                RxBuf[addr_1st-0x80]=0x00;
                RxBuf[(addr_1st-0x80)+1]=0x00;
            }
        }   
        else if(RxIn==0x0D){               //Если нажата кнопка Eter
            putsUSART(RxBuf);
            while(BusyXLCD());
            SetDDRamAddr(0x80);            //чистим строку
            for(i=0;i<16;i++){
               while(BusyXLCD());
               WriteDataXLCD(' ');
            }
            addr_1st=0x80;
            for(i=0;i<17;i++)             //Чисим буфер
                RxBuf[i]=0x00;
        }  
        else{                        
            while(BusyXLCD());           
            SetDDRamAddr(addr_1st++);
            WriteDataXLCD(RxIn);           //байт на LCD
        }
    }
}
//******************************************************************
void MyLowInt(void){
}
//Функция возвращает адрес массива символов в памяти, куда запишет
//преобразованные в строку шеснадцатиричные значения байтов в массиве,
//переданном ей параметром sour(адрес массива байтов, который надо преобразовать)
//len -колличество байтов в sour,dest-адрес массива, куда запишем преобразованную
//строку, равную len*2.
char* HexToASCII(char *sour,char *dest,int len)
{
    int i;
    char tmp;
    int j=0;
    for(i=0;i<len;i++){
        tmp=((sour[i]>>4)&0x0f);
        if(tmp<=9){
            dest[j]=tmp+0x30;
        }
        else if(tmp>=0x0A && tmp<=0x0F){
            dest[j]=tmp+0x37;
        }
        j++;
        tmp=sour[i];
        tmp=tmp &= 0x0f;
        if(tmp<=9){
            dest[j]=tmp+0x30;
        }
        else if(tmp>=0x0A && tmp<=0x0F){
            dest[j]=tmp+0x37;
        }
        j++;
        
        
    }
    dest[j]=0;
    return dest;
}
void ClrLCD(char addr){
    int i;
    while(BusyXLCD());
    SetDDRamAddr(addr);
    for(i=0;i<16;i++){
        while(BusyXLCD());
        WriteDataXLCD(' ');
    }
}
void  StrToLCD(char* str,char addr,char delay){
    ClrLCD(addr);
    while(BusyXLCD());
    SetDDRamAddr(addr);
    putsXLCD(str);
    Delay10KTCYx(delay);
}



 

НАЗАД                                                                                                                                         ДАЛЕЕ


Back to Top