Эксперимент #1.18 UART в PIC18 XC8

В общем думал я, что все будет по накатанной идти, а оказалось, что в использовании уарта в 18 серии есть некоторые различия.

Компилятор XC8 имеет набор встроенных функций для работы с периферией, найти заголовочные файлы можно в папке /includes/plib. Нас на данный момент интересует usart.h.

Сформулируем интересующие нас задачи:

  • Запись одного символа
  • Запись строки
  • Чтение одного символа = 1 байта
  • Чтение нескольких байт

Все это дело будем пробовать завести на PIC18F14K50.

Сначала я приведу картинки из протеуса, а уже файнал вершн будет в конце на реальной тестовой плате.

Поехали 🙂

Первым делом нужно как водится настроить уарт. Для этого существует функция OpenUSART(Config, baud), где Config это преднастройка по своему виду похожая на __CONFIG, а baud коэффициент, используемый при расчете бодрейта:

FOSC/[16 (baud+1)]

Попытаемся рассчитать этот коэффициент для бодрейта 9600 и кварца 12 МГц

baud = 12M/(9600*16) – 1 = 77.125 ~ 77

По поводу настроек конфига рекомендую обратиться в файлик pic18_plib.pdf, лежит в директории docs компилятора XC, там все очень подробно и прекрасно расписано.

Начнем с теста записи одного байта, для этого предназначена функция:

void WriteUSART(char data)

Ну вроде как все довольно прозрачно, пора практиковаться:

#include <xc.h>
#include <plib/usart.h>

#define _XTAL_FREQ 12000000 //The speed of your internal(or)external oscillator

#pragma config WDTEN = OFF, LVP = OFF, FOSC = HS

unsigned char UARTConfig = 0; baud = 0;
char txbyte = 'U';         //наш байт для пересылки

void main()
{
 TRISB7 = 0; //TX pin set as output
 TRISB5 = 1; //RX pin set as input

 UARTConfig = USART_TX_INT_OFF & USART_RX_INT_OFF & USART_ASYNCH_MODE & USART_EIGHT_BIT & USART_BRGH_HIGH ;    //прерывания выключены, асинхронный режим, 8 бит, высокоскоростной режим
 baud = 77;                 //Focs/(9600*16) - 1
 OpenUSART(UARTConfig,baud);

 WriteUSART(txbyte);        //Пишем наш байт

 while(1)
  {
  }//while(1)

}//main

*Решил обновить подсветку синтаксиса, что-то меня старая начала угнетать 🙂

Тест:

Дальше надо бы строку научиться пулять 🙂

Для этих целей есть готовая функция:

void putsUSART( char *data)

Не знаю как у вас, а у меня сразу возник вопрос, можно ли с помощью этой функции передавать служебные символы, а так же как ей вывести полученные численные данные.

Ответ на первый вопрос – да, можно вводить любые служебные символы (типа \r\n) все отрабатывается замечательно, с числами сложнее, это не готовая функция printf.

Но не стоит расстраиваться, зато мы не тратим много памяти, и для чисел всегда можно написать готовую функцию для конвертации:

void NumToUart(unsigned int Num) //Число в уарт
{

 unsigned int bignum = 10000;    //максимальный делитель
 unsigned char numtemp = 5;      //максимальное количество разрядов

while(numtemp>0)                 //Определяем сколько разрядов имеет наше число
 {
   if (Num/bignum)
     break;
   numtemp--;
   bignum = bignum / 10;
 }

for (unsigned char i = numtemp; i>0; i--)
 {
    WriteUSART( (Num - (Num/(bignum*10))*bignum*10 )/bignum + '0'); //Выталкиеваем все разряды - от старшего к младшему
    while(BusyUSART());                                             //Ждем пока освободится модуль иначе будут прострелы
    bignum = bignum/10;
 }

}

Кстати если не применять макрос while(BusyUSART()); есть шанс потерять кучу первых байт.

А теперь попытаемся поэкспериментировать с нашей новой функцией и просто с выводом строки

putsUSART( (char *) "Welcome to Diymicro.ru \r\n" );
putsUSART( (char *) "The number is " );
NumToUart(1283);

И то, что получилось:

Вроде как работает, можно двигаться дальше.

Чтение.

Я юзаю pic18f14k50, у которого RB5 аналоговый канал, то есть сначала нужно выключить аналоговые буферы.

ANSEL = 0;
ANSELH = 0;

И можно работать 🙂

Как обычно есть два пути – через *опу и прерывания, но так как у нас эксперимент, мы пойдем первым путем 🙂

Считать один байт можно с помощью функции

char ReadUSART(void)

То есть, например, это можно организовать как-то так:

while(!DataRdyUSART());
rxbyte=ReadUSART();
while(BusyUSART());

И все, теперь ваш байт из уарта находится в переменной rxbyte. Опять таки нужен первый макрос, иначе регистр не очистится и в терминал постоянно будет сыпаться его содержимое (для случая работы в цикле).

Если хочется считать побольше инфы (например длинное сообщение), то можно пользоваться функцией:

void getsUSART(char *buffer, unsigned char len)

Она считает ровно ваши len байт.

Придумать как отобразить работу чтения без видео я как-то не очень смог, так что придется вам показать еще и видео (:

Здесь разместим код, того, что буду показывать:

#include <xc.h>
#include <plib/usart.h>

#define _XTAL_FREQ 12000000 //The speed of your internal(or)external oscillator

#pragma config WDTEN = OFF, LVP = OFF, FOSC = HS  

unsigned char UARTConfig = 0;
unsigned char baud = 0;
char txbyte = 'U';         //наш байт для пересылки
unsigned char Rxdata[10];

void NumToUart(unsigned int Num)                                                //Число в уарт
{

  unsigned int bignum = 10000;
  unsigned char numtemp = 5;

  while(numtemp>0)                                                             //Определяем сколько разрядов имеет наше число
  {
    if (Num/bignum)
        break;
    numtemp--;
    bignum = bignum / 10;
  }  

  for (unsigned char i = numtemp; i>0; i--)
    {
      WriteUSART( (Num - (Num/(bignum*10))*bignum*10 )/bignum + '0');         //Выталкиеваем все разряды - от старшего к младшему
      while(BusyUSART());                                                       //Ждем пока освободится модуль иначе будут прострелы
      bignum = bignum/10;
    }

}

void main()
{

 ANSEL = 0;                                                                   //Отключаем аналоговые буфера
 ANSELH = 0;

 TRISB7 = 0; //TX pin set as output
 TRISB5 = 1; //RX pin set as input
 TRISB6 = 1; //RX pin set as input

 UARTConfig = USART_TX_INT_OFF & USART_RX_INT_OFF & USART_ASYNCH_MODE & USART_EIGHT_BIT & USART_BRGH_HIGH ;    //прерывания выключены, асинхронный режим, 8 бит, высокоскоростной режим
 baud = 77;                 //Focs/(9600*16) - 1
 OpenUSART(UARTConfig,baud);

 WriteUSART(txbyte);        //Пишем наш байт
 putsUSART( (char *) "\r\nputs test\r\n" );
 putsUSART( (char *) "Welcome to Diymicro.ru\r\n" );
 putsUSART( (char *) "The number is " );
 NumToUart(1283);

 getsUSART(Rxdata, 10);
 while(BusyUSART());
 putsUSART( (char *) "\r\nAll bytes have recieved, your message is - \r\n" );
 putsUSART(Rxdata);

 putsUSART( (char *) " \r\nNow, just print what you want :) \r\n" );

 while(1)
 {
      while(!DataRdyUSART());
      txbyte=ReadUSART();
      while(BusyUSART());
      if (txbyte)
        {
          WriteUSART(txbyte);
          while(BusyUSART());
        }

  }//while(1)

}//main()

Ну а здесь уже собственно результат:

Ну и как обычно, исходники можно найти в репе.

Эксперимент #1.18 UART в PIC18 XC8: 15 комментариев

  1. я про готовую библиотеку и не подумал даже, все по минимому:

    SPBRG = 77;//устанавливаем скорость для USART 9600 при 12*4=48Mhz
    RCSTA = 0b10010000;//регистр состояния и управления приемника USART
    TXSTA = 0b00100000;//регистр состояния и управления передатчика USART
    BAUDCON = 0b01000000;

    unsigned char read(void)
    {
    do{;}while(PIR1bits.RCIF == 0);
    return RCREG;
    }

    void write (unsigned char tmp)
    {
    TXREG = tmp; //записываем байт в регистр TXREG
    do{;}while(TXSTAbits.TRMT == 0);//ждем пока в TSR есть данные
    }

      • в компиляторе 3.46 нельзя выводить строки как обычно, например как у тебя : putsUSART( (char *) “Welcome to Diymicro.ru \r\n” );

        нужно создать массив с символами,а потом выводить его.
        не помню в каком проекте эксперементировал,поэтому и не написал пример.
        на тот момент когда занимался USARTом в PIC18 про XC8 не знал

        • Не так я и говорю, что нужно тогда еще функцию по выталкиванию строки в ручную писать, чтобы уже полноценная либа была.

          Но раз нам все дают готовое прямо в компиляторе, то зачем нам этот гемор? 🙂

  2. интересно было самому написать, да и вывод строки через массив это не такая уж и проблема. не нравится мне когда много всего написано,нравится когда по минимому, пусть даже есть небольшие неудобства.плюс если пытаться самому допирать, то в других местах уже будет проще, ну конечно далеко не везде сам допираешь 🙂

    • Внутренний RC в смысле?
      Если да так он почти во всех есть, просто у меня привычка чтоли, что если могу заложить получше возможность, то закладываю.

      • не 12MHz а 16MHz.не совсем уверен что RC.тот регистр который отвечает за выбор частоты называется OSCILLATOR CONTROL REGISTER,там что то про RC совсем ни слова.
        у данного мк и так портов не много, так еще и пару на кварц отдать надо

  3. Здравствуйте.
    Подскажите пожалуйста #pragma config WDTEN = OFF, LVP = OFF, FOSC = HS
    так биты конфигурации настраиваются в XC8 или же #FUSES? Или и так, и так можно?
    Что-то на __CONFIG у меня просто ругается…..
    ПРичем если я так напишу, то при запуски проекта в более старой версии MPLAB (не X), то это будет выводиться ошибкой и там только через __CONFIG надо будет, т.к. компилятор хайтековский, а не XC8?

    • #pragma config – XC8
      __CONFIG – hi-tech PICC compiler

      Читаем мануал на XC8 и видим:

      The 8-bit compilers have used the __CONFIG() macro for some targets that did not
      already have support for the #pragma config.
      For the 8-bit compilers, change any occurrence of the __CONFIG() macro, such as
      __CONFIG(WDTEN & XT & DPROT)
      to the #pragma config directive, as in
      #pragma config WDTE=ON, FOSC=XT, CPD=ON
      No migration is required if the #pragma config was already used

  4. Аааа. Я ничего не понимаю на английском 🙂 Поясните пожалуйста. А почему тогда на #fuses не ругается?
    А остальное все так же как и в хайтеки? include и т.д. у остального. Только вместо __CONFIG #pragma confiп?

    • Да, #pragma config

      Не совсем все, к портам доступ по другому организован и названия регистров могут отличаться, но это все решается в считанные секунды.

      Почему не ругается на фьюзы – понятия не имею, но упоминания о такой директиве нету в мануале на xc8.

      • Подскажите пожалуйста как мне такое написать?

        __CONFIG(WDTDIS & UNPROTECT & MCLRDIS & INTOSCIO);
        чтобы таймер отключить, чтоб работало от внутреннего генератора и чтоб был сброс при малом питании… Не знаю что такое LVP у Вас.
        #pragma config MCLRE = OFF — так у меня нормально, но вот когда хочу другие опции записать… Все ругается….

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.