![](templates/subSilver/images/CCSLogo.jpg) |
![CCS C Software and Maintenance Offers](templates/subSilver/images/forumAd6.jpg) |
View previous topic :: View next topic |
Author |
Message |
Wieger
Joined: 10 Jun 2007 Posts: 2
|
RS232 communication (PIC16F877) |
Posted: Sun Jun 10, 2007 11:47 am |
|
|
Hello,
For a school project we were asked to make a small boat that can be controlled via Bluetooth.
On our boat we use a PIC16F877 microcontroller to receive the steering information that's sent via Bluetooth (with the use of Java and a joystick).
Because our Bluetooth module responds in the form of <CR><LF>response<CR><LF> we filter the carriage returns and linefeeds, and interpret the data between it.
We actually look at the first letter of "response" to establish what to do. The Bluetooth steering data consists of a letter (L (left), R (right), B (backwards), F (forward)) and a number specifying the 'intensity'. With that number we send something to some ports, which causes the motor's speed to change, or the 'boat-steer-thingy' to rotate.
But we have been having a problem for 1.5 week or so with the RS232 communication.
As it seems now, there are much garbage characters being both received and sent by the microcontroller. But occasionally good data will be passed.
In the (small) school example code INT_RDA is used for receiving data. But because the port of that is used by something else we use port B7 for receive and port B6 for transmitting, so we use INT_RB for receiving data.
There are also two RS232 ports. One for debugging purposes, and the other one for the Bluetooth module.
Does anyone see something wrong with this code? Is it wrong to use INT_RB? Or do we have to do something extra when using INT_RB? Should we configure things differently?
I don't have the MPLAB IDE and CCS we have at school at hand. So I don't know which compiler version we have exactly. But it's definitely not a new version.
I'm not a really electronically technical person, by the way. It's a multidisciplinary project. I'm one of the two Software Engineers.
Any help's very much appreciated!
Code: |
/* IDP Project: groep 11 */
#device PIC16F877 adc=8
#include "16f877.h"
#include <STDLIB.H>
#define HIGH_START 114
#fuses HS,NOWDT,NOPROTECT
#use delay(clock=4000000) // 4 MHz clock
#use rs232(baud=9600, xmit=PIN_b6, rcv=PIN_b7, stream=bt) // Seriele communicatie bt module
#use rs232(baud=9600, xmit=PIN_c6, rcv=PIN_c7, stream=cpu) // Seriele communicatie mc
#use fast_io(a)
#use fast_io(b)
#use fast_io(c)
#use fast_io(d)
#use fast_io(e)
// PROTOCOL VOOR ONTVANGEN BERICHTEN
const char CONNECT = 'C';
const char OK = 'O';
const char ERROR = 'E';
const char LEFT = 'L' ;
const char RIGHT = 'R';
const char FORWARD = 'F';
const char BACKWARD = 'B';
const char WEAPON = 'W';
const char POWER = 'P';
const char STATUS = 'S';
// Globale constanten.
const int8 bufsize=50; // Maximale grootte van de strbuf[] array.
const tijd = 3; // ingestelde tijd hoe lang de poorten hoog zijn
// voor aansturing van het wapen.
// Globale variabelen.
int8 i; // Pointer strbuf[] array.
char strbuf[bufsize]; // Data karakters op rs232 poort bufferen.
long t_high, t_low, ms; // kompas tijd: begin en eind.
float dir // kompas resultaat, richting.
int1 polarity; // polariteit van de motor voor de vaarrichting
// true = forward, false = backward.
int1 connected; // geeft aan of er al connectie gemaakt is via bt.
int1 compas_verzonden; // geeft aan of er al een richting is verzonden
// naar de Wal.
int8 stuur; // stuur
int8 vlag; // stuur: vlag
int1 do_wapen; // wapen
#INT_TIMER0
void servo() {
/*
* Omschrijving: Frequentie voor de servo regelen.
* Argumenten: -
* Return: -
* Commentaar: De frequentie van de servo is 50 Hertz. De richting van de servo,
* het roer, wordt bepaald aan de hand van hoe lang er een hoog
* signaal wordt gegeven.
*/
if (vlag <= stuur)
output_HIGH(PIN_e1);
if (vlag >= stuur)
output_LOW(PIN_e1);
if (vlag > 40)
vlag=0;
vlag++;
set_timer0(1500);
}
void switch_polarity() {
/*
* Omschrijving: Polariteit omzetten.
* Argumenten: -
* Return: -
* Commentaar: De polariteit bepaald de vaarrichting van de boot.
*/
polarity = !polarity;
if (polarity)
OUTPUT_low(PIN_e2);
else
output_high(PIN_e2);
}
void do_left(byte b) {
/*
* Omschrijving: Stuurt roer naar links.
* Argumenten: richting in bytes.
* Return: -
* Commentaar: De hoeveelheid de er wordt gestuurd wordt bepaald aan de hand van
* de groote van de byte b. Er wordt in zes stappen gemeten, waarbij
* stuur is dertien rechtdoor is.
* Tevens wordt er nadat er roer is gegeven het compas vlaggetje
* weer op nul gezet, waardoor er een nieuw bericht wordt verzonden
* naar de wal met de laatste richting, dir.
*/
//printf("Do left: %x\n", b);
if (b < 20)
stuur = 13;
else if (b < 40)
stuur = 12;
else if (b < 60)
stuur = 11;
else if (b < 80)
stuur = 10;
else if (b < 100)
stuur = 9;
else
stuur = 8;
compas_verzonden = 0; // 0 is opnieuw verzenden van compas
}
void do_right(byte b) {
/*
* Omschrijving: Stuurt roer naar rechts.
* Argumenten: richting in bytes.
* Return: -
* Commentaar: De hoeveelheid de er wordt gestuurd wordt bepaald aan de hand van
* de groote van de byte b. Er wordt in zes stappen gemeten, waarbij
* stuur is dertien rechtdoor is.
* Tevens wordt er nadat er roer is gegeven het compas vlaggetje
* weer op nul gezet, waardoor er een nieuw bericht wordt verzonden
* naar de wal met de laatste richting, dir.
*/
//printf("Do right: %x\n", b);
if (b < 20)
stuur = 13;
else if (b < 40)
stuur = 14;
else if (b < 60)
stuur = 15;
else if (b < 80)
stuur = 16;
else if (b < 100)
stuur = 17;
else
stuur = 18;
compas_verzonden = 0; // 0 is opnieuw verzenden van compas
}
void do_forward(byte b) {
/*
* Omschrijving: Zet vaarrichting en snelheid.
* Argumenten: snelheid in bytes.
* Return: -
* Commentaar: Eerst wordt er getest of de polariteit zo nodig moet worden omge-
* zet. Vervolgens wordt de duty van de pwm gezet met byte b. Dit is
* de snelheid van de motor.
*/
if (!polarity) { // als de polariteit op achteruit staat:
switch_polarity();
}
set_pwm1_duty(b);
}
void do_backward(byte b) {
/*
* Omschrijving: Zet vaarrichting en snelheid.
* Argumenten: snelheid in bytes.
* Return: -
* Commentaar: Eerst wordt er getest of de polariteit zo nodig moet worden omge-
* zet. Vervolgens wordt de duty van de pwm gezet met byte b. Dit is
* de snelheid van de motor.
*/
if (polarity) { // als de polariteit op vooruit staat:
switch_polarity();
}
set_pwm1_duty(b);
}
void do_weapon(boolean b) {
/*
* Omschrijving: Zet wapen vlaggetje.
* Argumenten: vlaggetje byte b. Had eerst de bedoeling om het wapen aan en uit
* de doen, maar is later veranderd naar alleen een vlaggetje
* zetten.
* Return: -
* Commentaar: Zet wapen vlaggetje waardoor het wapen wordt uitgevoerd in de
* main.
*/
do_wapen = 1;
}
void do_power(boolean b) {
/*
* Omschrijving: Zet power vlaggetje.
* Argumenten: vlaggetje byte b. Had eerst de bedoeling om de power aan en uit
* de doen, maar is later veranderd naar alleen een vlaggetje
* zetten.
* Return: -
* Commentaar: Zet wapen vlaggetje waardoor het wapen wordt uitgevoerd in de
* main.
*/
//pin ff hoog voor uitschakelen spanning, 200ms?
set_pwm1_duty(0);
}
void get_direction() {
/*
* Omschrijving: Berekend de vaarrichting van de boot.
* Argumenten: -
* Return: -
* Commentaar: Er wordt gewacht zolang de input van het kompas hoog is,
* vervolgens zolang het laag is en dan wordt de timer op nul
* gezet, dus bij het begin van een periode.
* Vervolgens wordt er weer gewacht zolang het inputsignaal van
* het kompas hoog is en wordt er vervolgens de tijd van hoelang
* het signaal hoog was opgeslagen.
* Vervolgens wordt op dezelfde manier gemeten hoelang het lage
* signaal is.
*/
//printf("Compass CMPS03\n");
while (input(PIN_c1));
while (!input(PIN_c1));
set_timer1(0);
while (input(PIN_c1));
t_high = get_timer1();
set_timer1(0);
while (!input(PIN_c1));
t_low = get_timer1();
ms = (float) t_low/65; // laag pulse is altijd 65ms
// => ms is aantal timer pulsen per ms
dir = (float) t_high/ms-1; // hoog pulse breedte in ms min 1
// is 1/10 van de hoek
// in graden.
dir = 2.22;
//printf("Direction: %f\n", dir);
}
void protocol(char cmd[]) {
/*
* Omschrijving: Afhandelen van de ontvangen commando's bluetooth IC of walsysteem.
* Argumenten: commando string.
* Return: -
* Commentaar: -
*/
switch(cmd[0]) {
case CONNECT:
printf("Connected\n");
break;
case OK:
printf("OK\n");
break;
case ERROR:
printf("Error\n");
break;
case LEFT:
do_left(cmd[1]);
//output_d(0x02); // led 2 aan
break;
case RIGHT:
do_right(cmd[1]);
//output_d(0x04); // led 3 aan
break;
case FORWARD:
do_forward(cmd[1]);
//output_d(0x08); // led 4 aan
break;
case BACKWARD:
do_backward(cmd[1]);
//output_d(0x10); // led 5 aan
break;
case WEAPON:
do_weapon(cmd[1]);
//output_d(0x20); // led 6 aan
break;
case POWER:
do_power(cmd[1]);
//output_d(0x40); // led 7 aan
break;
case STATUS:
//output_d(0x80); // led 7 aan
break;
default:
//printf("unknown cmd: %S\n", cmd[0]);
}
}
#INT_Rb
void rs232(void) {
/*
* Omschrijving: Inlezen van data karakters op de rs232 bufferpoort
* Argumenten: -
* Return: -
* Commentaar: De data strings worden gescheiden door <cr> en <lf>.
* Deze karakters worden uit de data gefilterd. De data string
* die overblijft wordt als parameter meegegeven aan de procedure
* protocol.
*/
char chr; // Uitgelezen karakter (letter).
disable_INTERRUPTS(GLOBAL);
while (kbhit()) {
// Filter de <cr> en <lf> uit de data.
if (((chr=getc())!='\n')&&(chr!='\r')&&(i<(bufsize-1)))
strbuf[i++]=chr;
else {
if ((chr=='\r')) {
strbuf[i]='\0';
// Controlleer of er wel data in de strbuf staat
// ivm commando's Bluetooth IC,
// deze beginnen namelijk met <cr><lf> en
// eindigen ook met <cr><lf>.
if(i > 0)
protocol(strbuf);
i = 0;
}
// Voorkom bufsize overflow van array.
if (i >= bufsize-1)
i = 0;
}
}
ENABLE_INTERRUPTS(GLOBAL);
}
void bluetoothinitialisatie(void) {
/*
* Omschrijving: Instellen van bluetooth IC.
* Argumenten: -
* Return: -
* Commentaar: Resetten van bluetooth IC en AT commando's sturen naar IC.
* Door een key en een bluetooth adres in te stellen is het onmogelijk om
* het bootsysteem door een ander systeem over te nemen.
*/
output_d(0x10);
output_b(0x00); // Resetten van bluetooth IC. Gebruik reset pin bluetooth IC.
delay_ms(2000); // Minimaal 1 seconden resetten, hier gebeurt het 2 seconden.
output_b(0x20);
}
void bluetoothcommunicatie(void) {
/*
* Omschrijving: communicatie opzetten.
* Argumenten: -
* Return: -
* Commentaar: verbinding maken met de bluetooth adres.
*/
disable_interrupts(GLOBAL);
fprintf(BT, "\n\rat+btname=plankjegas\n\r"); // naam vastellen bt module
fprintf(BT, "\n\ratd00A0961d2FF4\n\r"); //connecten met walsysteem
delay_ms(100);
enable_interrupts(INT_Rb);
enable_interrupts(GLOBAL);
}
void main(void) {
set_tris_a(0xCF); //1100 1111
set_tris_b(0x8f); //1000 1111 (b0 input)
set_tris_c(0x93); //1000 0011 (alle lijnen input)
set_tris_d(0x00); //0000 0000 (alle lijnen output)
set_tris_e(0xE8); //1110 1000 (b4=0: No PSPMODE!)
// declareren
int x; // wapen: teller
// initialiseren
x = 0;
connected = 0;
vlag = 0;
compas_verzonden = 1;
do_wapen = 0;
i = 0;
// MOTER van de voortstuwing instellen. (frequentie)
setup_ccp1(CCP_PWM); // Pwm voor aandrijving
setup_timer_2(T2_DIV_BY_4, 127, 16); // frequentie voor pwm signaal +/- 500Hz
setup_timer_0 (RTCC_DIV_2|RTCC_INTERNAL); // set timer voor servo mtr
setup_timer_1(T1_INTERNAL | T1_DIV_BY_8); // TIMER voor compas
enable_interrupts(INT_TIMER0); // Timer voor servo v/h sturen
enable_interrupts(GLOBAL);
while(true) {
if (connected == 0) {
bluetoothcommunicatie();
delay_ms(100);
}
if (compas_verzonden == 0) {
get_direction();
disable_interrupts(INT_Rb);
fprintf(BT, "\rD%f\r", dir); // Verzend richting via BT.
enable_interrupts(INT_Rb);
compas_verzonden = 1; // Kompas vlaggetje op true zetten.
}
if (do_wapen == 1) {
for(x = 0; x < 13; x++) {
output_d(0x01); // uitgang d1 hoog
delay_ms(tijd);
output_d(0x02); // uitgang d2 hoog
delay_ms(tijd);
output_d(0x04); // uitgang d3 hoog
delay_ms(tijd);
output_d(0x08); // uitgang d4 hoog
delay_ms(tijd);
}
do_wapen = 0; // Wapen vlaggetje op true false zetten.
}
}
}
| [/code] |
|
![](templates/subSilver/images/spacer.gif) |
PCM programmer
Joined: 06 Sep 2003 Posts: 21708
|
|
Posted: Sun Jun 10, 2007 12:51 pm |
|
|
Quote: |
#include "16f877.h"
#include <STDLIB.H>
#define HIGH_START 114
#fuses HS,NOWDT,NOPROTECT, NOLVP
|
Add NOLVP to your #fuses statement as shown in bold.
Quote: |
#INT_Rb
void rs232(void) {
char chr;
disable_INTERRUPTS(GLOBAL);
ENABLE_INTERRUPTS(GLOBAL); }
}
|
You don't have to enable/disable global interrupts in any interrupt routine.
The PIC automatically does this for you in hardware. In fact, it's
dangerous to re-enable Global interrupts inside an interrupt routine.
Nested interrupts are not supported for the 16F series PICs.
Delete those two lines.
Quote: |
Is it wrong to use INT_RB?
|
The standard method is to use INT_EXT (on pin B0) for the Rx pin
of a software UART. |
|
![](templates/subSilver/images/spacer.gif) |
Wieger
Joined: 10 Jun 2007 Posts: 2
|
|
Posted: Sun Jun 10, 2007 1:25 pm |
|
|
Thanks, I hope this will help.
But what exactly is the effect of the NOLVP (no low-voltage programming) option (or the missing of it)?
And why is using INT_EXT the standard method? |
|
![](templates/subSilver/images/spacer.gif) |
arunb
Joined: 08 Sep 2003 Posts: 492 Location: India
|
RE: |
Posted: Sun Jun 10, 2007 2:38 pm |
|
|
Hi,
Technically it is possible to use INT_EXT or INT_RB for detecting USART characters, I have also used them in my applications, but I I have found that they work well in low baud rates only, as the baud rate is increased, more and more garbage is received. Also you must put a delay_us() statement in the interrupt routine.
for eg:
int_ext
usart2()
{
delay_us(100)
cData=getc(UART2_STERAM);
}
thank
arunb |
|
![](templates/subSilver/images/spacer.gif) |
Humberto
Joined: 08 Sep 2003 Posts: 1215 Location: Buenos Aires, La Reina del Plata
|
|
Posted: Sun Jun 10, 2007 5:43 pm |
|
|
To use PB on change interrupt feature be aware that:
1) Any pin PB4...7 will fire the interrupt. Be sure that all the 3 "unused"
PortB pins should be in steady state.
2) It is the programmer responsibility to clear the interruption, it is not cleared
"automatically" like another interrupts.
To clear the interrupt, you must do a portB read operation inside the interrupt.
Add: x = portB;
inside the interrupt handler, if not you will receive only one of the expected char.
Humberto |
|
![](templates/subSilver/images/spacer.gif) |
ckielstra
Joined: 18 Mar 2004 Posts: 3680 Location: The Netherlands
|
|
Posted: Mon Jun 11, 2007 12:53 am |
|
|
Quote: | But what exactly is the effect of the NOLVP (no low-voltage programming) option (or the missing of it)? | 95% of the chip programmer devices are of the high voltage type.
The low voltage programmers require the PGM (B3 on the 16F877) so this can't be used in your application for other purposes. With LVP active an (accidental) low voltage on the PGM input activates the programming mode stalling your processor. Setting the NOLVP fuse saves you these potential problems.
Your protocol() function is calling printf. Calling printf from inside an interrupt routine shouldn't be done as it takes a long time and you are going to miss received data. |
|
![](templates/subSilver/images/spacer.gif) |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
Powered by phpBB © 2001, 2005 phpBB Group
|