1.26.2014

UART - Realizacja komunikacji 1-Wire z termometrem DS18B20 - EK-TM4C123GXL


Mikro-kontroler TM4C123GH6PM posiada 8 modułów UART - Universal Asynchronous Receiver/Transmitter. Do obsługi transmisji 1-Wire należny przygotować wyjście TX i wejście RX tak aby sygnały były współdzielone z jednym przewodem na którym musi być oparta cała transmisja. Do fizycznego połączenia pinów RX i TX potrzebne są dwa tranzystory NPN BC547 i dwa rezystory, przy testach i pisaniu kodu korzystałem z dokumentacji Atmel  i Maxim integrated.


Testowa płytka stykowa połączona z lanunchpadem.



Do inicjacji UART służy poniższa funkcja, nie wykorzystałem przerwań i buforów FIFO. Należy  w opcjach projektu  dodać "predefined symbols" "PART_LM4F230H5QR", gdyż bez tego biblioteka nie zwróci odpowiednich map dla naszego procesora. W zależności od płytki może być potrzebny inny zapis, więcej na forum TI link.

 
void OneWire_init(void)
{
  // Enable Peripheral Clocks
 SysCtlPeripheralEnable(SYSCTL_PERIPH_UART1);
 SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOB);
 // Enable pin  for UART URX
 GPIOPinConfigure(GPIO_PB0_U1RX);
 GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_0);
 // Enable pin  for UART UTX
 GPIOPinConfigure(GPIO_PB1_U1TX);
 GPIOPinTypeUART(GPIO_PORTB_BASE, GPIO_PIN_1);

 UARTClockSourceSet(UART1_BASE, UART_CLOCK_SYSTEM);
 UARTConfigSetExpClk(UART1_BASE, SysCtlClockGet(), 115200, (UART_CONFIG_WLEN_8|UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
 UARTFIFODisable(UART1_BASE);
 UARTEnable(UART1_BASE);
}

Podstawowe funkcje jakie będą niezbędne do obsługi 1-Wire na wyższym poziomie to wczytywanie 1, wczytywanie 0, odczyt jednego bitu i reset.  W jednym bajcie przekazywany jest tylko jeden bit, cała ramka jest potrzebna do spełnienia czasowego wymogu transmisji. Baud rate ustawione w większości operacji na 115200.
Po nadaniu bajtu funkcja czeka aż bajt zostanie wysłany przez UART i następnie odebrany i odczytany (możliwe, że odczytanie jest zbędne, ma na celu oczyszczenie bufora).

Wczytanie 0 na magistrale, TX nadaje bajt składający się z samych zer.

void OneWire_write0()
{
 UARTCharPut(U_BASE, 0x00);
 while(UARTBusy(U_BASE))
 {
 }
 while(UARTCharsAvail(U_BASE))
 {
  UARTCharGetNonBlocking(U_BASE);
 }
}


Wczytanie 1 na magistrale, TX nadaje bajt składający się z samych 1.

void OneWire_write1()
{
 UARTCharPut(U_BASE, 0xFF);
 while(UARTBusy(U_BASE))
 {
 }
 while(UARTCharsAvail(U_BASE))
 {
  UARTCharGetNonBlocking(U_BASE);
 }
}

Odczytanie jednego bitu ze slave. Wczytanie 1 na magistrale i odczyt bajtu który przy zmodyfikowaniu przez slave będzie inny niż 1 co oznacza nadanie przez slave 0.

Read 0

Read 1

unsigned char OneWire_Read()
{
 // function reads 1-wire bus
 // return 1 or 0

 unsigned long tmp;
 UARTCharPut(U_BASE, 0xFF);
 while(UARTBusy(U_BASE))
 {
 }
 while(UARTCharsAvail(U_BASE))
 {
  tmp=UARTCharGetNonBlocking(U_BASE);
 }
 return (0xFF==tmp);
}

Dodatkową ważną funkcja jest reset, musi być wykonany przy niższym baud rate = 9600.
Polega na wczytanie 0xF0 na magistrale i odczycie bajtu który przy zmodyfikowaniu przez slave będzie inny niż 0xF0 co oznacza, że jest działający slave na magistrali.

Reset - brak slave na magistrali.


Reset - slave odpowiedział.



unsigned char OneWire_Reset(void){

 // function reset bus and returns 1 if is presence pulse

 unsigned long tmp=0;
 UARTDisable(U_BASE);
    UARTConfigSetExpClk(U_BASE, SysCtlClockGet(), 9600, (UART_CONFIG_WLEN_8|UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
    UARTEnable(U_BASE);

 UARTCharPut(U_BASE, 0xF0);
 while(UARTBusy(U_BASE))
 {
 }
 while(UARTCharsAvail(U_BASE))
 {
  tmp=UARTCharGetNonBlocking(U_BASE);
 }

 UARTDisable(U_BASE);
    UARTConfigSetExpClk(U_BASE, SysCtlClockGet(), 115200, (UART_CONFIG_WLEN_8|UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE));
    UARTEnable(U_BASE);
 return (0xF0!=tmp);
}

1.11.2014

PWM sprzętowy - EK-TM4C123GXL

Mikro-kontroler TM4C123GH6PM posiada dwa moduły PWM, każdy moduł posiada cztery generatory, każdy generator generuje dwa sygnały oparte na wspólnym zegarze. W sumie jest 16 wyjść PWM. Generatory mogą być synchronizowane.

Prosty kod włączajacy sygnał PWM na piny PC4 i PC5. Należy  w opcjach projektu  dodać "predefined symbols" "PART_LM4F230H5QR", gdyż bez tego biblioteka nie zwróci odpowiednich map dla naszego procesora. W zależności od płytki może być potrzebny inny zapis, więcej na forum TI link

 
#include "utils/ustdlib.h"
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/pin_map.h"
#include "driverlib/debug.h"
#include "driverlib/hibernate.h"
#include "driverlib/gpio.h"
#include "driverlib/systick.h"
#include "driverlib/timer.h"
#include "driverlib/pwm.h"

int main(void)
{
 SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_OSC_INT);  // 40 MHz int
 SysCtlPWMClockSet(SYSCTL_PWMDIV_64); //PWM - pre-divided System Clock 625 kHz

 SysCtlPeripheralEnable(SYSCTL_PERIPH_PWM0);  // PWM0

 SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOC); // PWM PC4
        // Enable pin PC5 for PWM0 M0PWM7
        GPIOPinConfigure(GPIO_PC4_M0PWM6);
        GPIOPinTypePWM(GPIO_PORTC_BASE, GPIO_PIN_5);
        // Enable pin PC4 for PWM0 M0PWM6
        GPIOPinConfigure(GPIO_PC5_M0PWM7);
        GPIOPinTypePWM(GPIO_PORTC_BASE, GPIO_PIN_4);

 PWMGenConfigure(PWM0_BASE, PWM_GEN_3, PWM_GEN_MODE_DOWN| PWM_GEN_MODE_NO_SYNC); // PWM Count-Down Mode

 PWMGenPeriodSet(PWM0_BASE, PWM_GEN_3, 6249); // 100 Hz

 PWMPulseWidthSet(PWM0_BASE, PWM_OUT_6, 3124); // PC4 50%

 PWMPulseWidthSet(PWM0_BASE, PWM_OUT_7, 1562); // PC5 25%

 PWMGenEnable(PWM0_BASE, PWM_GEN_3);

 PWMOutputState(PWM0_BASE, (PWM_OUT_6_BIT | PWM_OUT_7_BIT), true);

 while(1)
 {
 }
}
Sygnał generowany na pinach PC4 i PC5.


Można ustawić odwrócenie sygnału na wyjściu.
Funkcja PWMOutputInvert(PWM0_BASE, PWM_OUT_7_BIT, true)

Invert


Można ustawić licznik PWM w opcji up-down. Przebieg na dwóch wyjściach generatora będzie uzależniony od zadziałania COMP podczas liczenia w górę i w dół, nie są brane  wartości licznika 0 i load. Obraz dwóch sygnałów można podzielić na symetryczne części.
Funkcja PWMGenConfigure(PWM0_BASE, PWM_GEN_3, PWM_GEN_MODE_UP_DOWN | PWM_GEN_MODE_NO_SYNC)

up-down timer


Można również ustawić tryb pracy "dead-band generator", jest wtedy brany tylko czas ustawiony dla COMPA  (wyjście 0 danego generatora)  i na jego podstawie generowane są dwa sygnały na wyjściach generatora. Do ustawienia jest czas martwy Rising Edge i Falling Edge.
Funkcja PWMDeadBandEnable(PWM0_BASE, PWM_GEN_3,300,100);

Dead-Band Generator

1.10.2014

Konfiguracja Watchdog EK-TM4C123GXL


Mikro-kontroler TM4C123GH6PM posiada dwa moduły watchdog, WDT0 i WDT1. Różnica miedzy modułami polega na korzystaniu z różnych zegarów, Watchdog Timer 0 korzysta z systemowego zegara a Watchdog Timer 1 z PIOS. Timer WDT1 posiada dodatkowy bit zdefiniowany w rejestrze Watchdog Timer Control (WDTCTL) który po zapisie jest zerowany, kolejna operacja na rejestrze może być wykonana po pojawieniu się jedynki w tym bicie. Dodatkowo z modułem  WDT1 związanych jest kilka błędów w samym krzemie i aby go uruchomić należy zastosować się do erraty wydanej przez producenta. Testowałem WDT1 i bez zapoznania się z erratą nie udało się zainicjować jego działania w standardowy sposób. Po dokonaniu zalecanych przez producenta operacji zadziałał.

Watchdog posiada:

  • 32-bitowy licznik z programowalna wartością startową
  • możliwość generowania przerwania, opcjonalnie nie maskowanego NMI
  • możliwość generowania resetu
  • możliwość zablokowania zmian w konfiguracji w trybie działania programu
  • możliwość ustawienia stopu timera podczas kroków debugowania




Aby uruchomić sprzętowy WDT należy w rejestrze Watchdog Timer Run Mode Clock Gating Control (RCGCWD) ustawić bit R0 lub R1. Funkcja SysCtlPeripheralEnable(moduł).

Jeżeli WDT ma zliczać od wartości innej niż pełne słowo 32bit należy wypełnić rejestr WDTLOAD wartością startową. Funkcja WatchdogReloadSet(wartość).

Jeżeli ma być generowany reset, należy ustawić bit RESEN  w rejestrze WDTCTL. Funkcja WatchdogResetEnable().

Można ustawić rodzaj przerwania, bit INTTYPE w rejestrze WDTCTL. 0=normal,1=NMI. Funkcja WatchdogIntTypeSet().

Należy włączyć WDT ustawiając bit INTEN w rejestrze WDTCTL. Przerwanie będzie włączone i WDT zacznie zliczanie. Bit INTEN można programowo ustawić tylko raz, potem zmiany będą ignorowane. Aby wyłączyć WDT należy wykonać reset sprzętowy lub zresetować programowo przez Watchdog Timer Software Reset (SRWD).

Można dodatkowo zablokować możliwość zmian w rejestrach należy wpisać 0x1ACC.E551 do rejestru  Watchdog Lock (WDTLOCK). Odczyt z rejestru zwraca 0 dla nie zablokowanego i 1 dla zablokowanego. Funkcja WatchdogLock().


Prosty kod z wykorzystaniem WDT0 bez generowania resetu, z włączoną obsługą przerwania. Funkcja WatchdogIntRegister() przypisuje wektor do przekazanej funkcji i automatycznie odblokowuje przerwanie globalne.


 

#include "utils/ustdlib.h"
#include "inc/hw_types.h"
#include "inc/hw_memmap.h"
#include "driverlib/sysctl.h"
#include "driverlib/pin_map.h"
#include "driverlib/debug.h"
#include "driverlib/hibernate.h"
#include "driverlib/gpio.h"
#include "driverlib/systick.h"
#include "driverlib/timer.h"
#include "driverlib/watchdog.h"

void WDT_interrupt(void); // WDT handler

int main(void)
{
 SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_OSC_INT);  // 40 MHz int

 //green LED on
 SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
 GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3);
 GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, 0x08);

 SysCtlPeripheralEnable(SYSCTL_PERIPH_WDOG0);

 WatchdogReloadSet(WATCHDOG0_BASE,0xFEEFEE);

 WatchdogStallEnable(WATCHDOG0_BASE); // debbuger

 WatchdogEnable(WATCHDOG0_BASE);

 WatchdogIntRegister(WATCHDOG0_BASE, WDT_interrupt ); // interrupt handler

 while(1)
 {
 }
}


void WDT_interrupt(){

 WatchdogIntClear(WATCHDOG0_BASE); // clear int
 static unsigned long fl; // flag blink led
 fl^=1; // toggle flag

 if(fl){
  GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, 0x08);
 }
 else{
  GPIOPinWrite(GPIO_PORTF_BASE,GPIO_PIN_1|GPIO_PIN_2|GPIO_PIN_3, 0x00);
 }
}