softwarový sér. port u AT89C51ED2

Asi ani neskusaj :slight_smile:
Pod prerusenim mas zase nejake casove slucky, ktore Ti este moze prekladac zoptimalizovat a pripadne aj z programu vyhodit (dovody som pisal vyssie).

Okrem toho, ako vidim na RX do MCU (teda TX zo Zigbee modulu) mas privedeny /wr a nie vstup od citaca casovaca.
Musis si byt 110% isty, co oznacenie TX a RX na Zigbee znamena. Normalne by to malo znamenat, ze do RX na module ide signal TX z MCU a signal z TX na module ma ist do RX na MCU.

Nerobim s x51, tak ti neviem napisat program s presnym oznacenim registorom v Tvojom MCU, ale princip je rovnaky.

  1. TX z modulu pripoj napr na vstup T0.
    potom si prepis nasledovny algoritmus. Je to pre ATmega. Netestujem extra start bit, preto je tam na zaciatku 1.5 nasobok Bd

SIGNAL (COM5_SIG_TEST_START_BITU) - funkcia, ktora sa zavola po preteceni casovaca, ked je nastaveny v mode pocitadla vst impulzov a ma nastavenu predvolbu na 0xff

toto su vsetko makra, ktore si musis ty napisat podla svojho, ich vyznam je jasny z nazvov
COM5_ZAKAZ_PRERUSENIA_OVERFLOW_TIMER;
COM5_SET_TIMER_RX_TX_BIT; // zapni prijem od compare match
COM5_POVOLENIE_PRERUSENIA_COMAPRE_MATCH;

SIGNAL (COM5_SIG_TIMER_RX_TX_BIT) - funkcia, ktora sa zavola, ked pride prerusenie od compare match


// Definicia LOCALnych funkcii

// identifikacia start bitu, prisla dobezna hrana signalu
SIGNAL (COM5_SIG_TEST_START_BITU)
{
	// nastav hodnotu compare match na 1.5 nasobok bitovej rychlosti a prerusenie od compare match
	SET(sw_status,ROZPRACOVANY_PRIJEM);
	COM5_PRAC_POCITADLO = 0x00;// prednastavenie hodnoty TCNT tak, aby pri najblizsiemu compare match doslo k preruseniu
	COM5_COMPAR_REGISTER = sw_predvolba_pre_1_5nasobok_bitu;
	sw_udr_prac = 0;
	sw_pocitadlo_bitov = 0;
	COM5_ZAKAZ_PRERUSENIA_OVERFLOW_TIMER;
	COM5_SET_TIMER_RX_TX_BIT;  // zapni prijem od compare match
	COM5_POVOLENIE_PRERUSENIA_COMAPRE_MATCH;

	return;
}


SIGNAL (COM5_SIG_TIMER_RX_TX_BIT)
{
	uint8_t prac;
	
	COM5_COMPAR_REGISTER = sw_predvolba_pre_1nasobok_bitu;


	sw_pocitadlo_bitov++;

	// bezi prijem
	if TST(sw_status,ROZPRACOVANY_PRIJEM) {

		// test stop bitu
		if (sw_pocitadlo_bitov > 8) {
			if (GET_RX5) {
				sw_udr = sw_udr_prac;

// ****************** zrusenie prerusenia po prijati bajtu ****************
				RES(sw_status,ROZPRACOVANY_PRIJEM);
				COM5_ZAKAZ_PRERUSENIA_COMAPRE_MATCH;
				COM5_PRAC_POCITADLO = COM5_MAX_HODNOTA_PRAC_POCITADLA;// prednastavenie hodnoty TCNT tak, aby pri najblizsej dobeznej hrane prislo k preruseniu		
				COM5_SET_TEST_START_BITU;  // zapni prijem
				COM5_RES_INT_FLAG;
				COM5_POVOLENIE_PRERUSENIA_OVERFLOW_TIMER;
				POVOLENIE_PRERUSENIA;
// ************************************************************************

				// inicializacia funkcie PAUSE, lebo bolo nieco prijate
				timer_com5.timer = COM5_TIMEOUT_RX;
				prg_tb_timer(TB_5ms, TIMER_INIT, (TIMER_STRUCT * ) &timer_com5);

#if (COM5_ECHO == TRUE)

				sprava_prijata = TRUE;	
				mem[cnt_mem_max] = sw_udr;
				if (cnt_mem_max < (MAX_MEM - 1)) {
					cnt_mem_max++;
				}

				RES(sw_status,ROZPRACOVANY_PRIJEM);
				RES(sw_status,ROZPRACOVANE_VYSIELANIE);
				COM5_ZAKAZ_PRERUSENIA_COMAPRE_MATCH;
				COM5_PRAC_POCITADLO = COM5_MAX_HODNOTA_PRAC_POCITADLA;// prednastavenie hodnoty TCNT tak, aby pri najblizsej dobeznej hrane prislo k preruseniu		
				COM5_SET_TEST_START_BITU;  // zapni prijem
				COM5_POVOLENIE_PRERUSENIA_OVERFLOW_TIMER;
				return;
				
				// priprav nacitanie dalsieho bajtu
#else
				com5_struct.pracovny_bajt = sw_udr;
				prac = prg_prijaty_bajt((COM_STRUCT *)&com5_struct);

				if (prac == COM_VYPNI_PRIJEM) {

				// ak bol pri novej sprave este stale zablokovany box, iniciuj ho
					SET(com5_struct.status,SPRACUJ_PROTOKOL);
					RES(sw_status,ROZPRACOVANY_PRIJEM);
					COM5_ZAKAZ_PRERUSENIA_COMAPRE_MATCH;
					return;
				}
				RES(sw_status,ROZPRACOVANY_PRIJEM);
				RES(sw_status,ROZPRACOVANE_VYSIELANIE);
				COM5_ZAKAZ_PRERUSENIA_COMAPRE_MATCH;
				COM5_PRAC_POCITADLO = COM5_MAX_HODNOTA_PRAC_POCITADLA;// prednastavenie hodnoty TCNT tak, aby pri najblizsej dobeznej hrane prislo k preruseniu		
				COM5_SET_TEST_START_BITU;  // zapni prijem
				COM5_POVOLENIE_PRERUSENIA_OVERFLOW_TIMER;
				return;
#endif		
			}
			// nebol stop bit
			else prg_com5(COM_ZAPNI_PRIJEM, 0);
		}
		else {
			sw_udr_prac >>= 1; // rotacia do prava
			if (GET_RX5) {
				sw_udr_prac |= 0x80;
			}
			// skratenie casu testu stop bitu
			if (sw_pocitadlo_bitov == 8) COM5_COMPAR_REGISTER = sw_predvolba_pre_0_8nasobok_bitu;

		}
		return;
	}

	if TST(sw_status,ROZPRACOVANE_VYSIELANIE) {
		// generovanie start bitu

		if (sw_pocitadlo_bitov == 1) {
#if (COM5_ECHO == TRUE)

			if (cnt_mem == cnt_mem_max) { // ukonci vysielanie
				prg_com5(COM_ZAPNI_PRIJEM, 0);
				return;
			}
			else {
				if (cnt_mem == 0) sw_udr = 's';
				else sw_udr = mem[cnt_mem];

				if (cnt_mem < cnt_mem_max) cnt_mem++;
			}
#else			
			sw_udr = prg_vyslany_bajt((COM_STRUCT *)&com5_struct);
#endif
			if TST(com5_struct.setup,PREPINAJ_SMER) { // je rezim RS485
				COM5_SMER_RS485_RX;
			}

			RES_TX5;
		}
		else {
			if (sw_pocitadlo_bitov < 10) {
				if (sw_udr & 0x01) SET_TX5;
				else RES_TX5;
				sw_udr >>= 1;
			}
				// generuj 3x stop bit
			if (sw_pocitadlo_bitov == 10) {
				SET_TX5;
			}
			// otestuj, ci treba este vysielat
			if (sw_pocitadlo_bitov == 13) {
				if TST(com5_struct.setup,PREPINAJ_SMER) { // je rezim RS485
					COM5_SMER_RS485_RX;
				}
				sw_pocitadlo_bitov = 0;
#if (!(COM5_ECHO == TRUE))
				// ak sa uz nema nic odvysielat, potom prepni UART na prijem
				if (!(TST(com5_struct.status,JE_VYSIELANIE))) {
					prg_com5(COM_ZAPNI_PRIJEM, 0);
				}
#endif
			}
		}
		
		return;
	}
}

Ale z toho asi velmi mudry nebudes :slight_smile:, to bolo iba na ukazku ako fragment z realnej sw komunikacnej rutiny. Nie je tam uplne vsetko a o tom kode ani nemusime polemizovat. Uviedol som to iba ako dokaz, ze viem o com pisem.

Takze este raz mnemotechnicky. Neviem, ci ma x51 compare match, budem teda pisat algoritmus ako by bolo prerusenie iba od pretecenia casovaca. Predpokladam, ze sprava je ukoncena znakom 0x0d


#define FALSE 0x00
#define TRUE 0xff
#define KONCOVY_ZNAK 0x0d
#define MAX_POCET_BAJTOV 100

uint8_t prijaty_bajt, cnt_pom, bufer[MAX_POCET_BAJTOV], aktual_index, sprava_ukoncena = FALSE; 

int main(void)
{
  fn_inicializacia_prijmu_bajtu();
  aktual_index = 0;
  // hlavna slucka, tusi rob co chces
  while(1) {

    if (sprava_ukoncena == TRUE) {
      fn_spracuj_protokol();
    }
    // ak neprisla sprava, kludne si volaj do kolecka svoje dalsie funkcie, napr na spracovanie displaya a tak podobne. Len sa cas od casu pozri, ci uz nahodou nebola prijata cela sprava.
  }
}


void fn_prerusenie_od_pretecenia(void) 
{
  // ak bola sprava ukoncena a je zle osetrene vypnutie prerusenia, tak z funkcie hned vyskoc
  if (sprava_ukoncena == TRUE) return;


  // normalna cinnost rutiny
  if (cnt_pom == 0) {
      - nastav T0 ako casovac
      - nastav do T0 predvolbu tak, aby sa po uplynuti casu 0.5Bd vyvolalo prerusenie
  }
  // ak cakas start bit
  if (cnt_pom == 1)  {
    //  a na pine T0 je naozaj nula, potom pokracuj
    if (!(PORT_P3 & (1<<pin_T0))) {
      - nastav do T0 predvolbu tak, aby sa po uplynuti casu 1 Bd vyvolalo prerusenie
    }
    // nejednalo sa o start bit, ale asi o nejaky sum
    else {
       fn_inicializacia_prijmu_bajtu();
       return;
    }
 }
 if (cnt_pom > 1) && (cnt_pom <= 9) {
    prijaty_bajt = prijaty_bajt<<1;
    if (PORT_P3 & (1<<pin_T0))) {
      prijaty_bajt = prijaty_bajt + 0x01;
 }
 // otestuj stop bit
 if (cnt_pom == 10) {
    if (PORT_P3 & (1<<pin_T0))) {
        bufer[aktual_index] = prijaty_bajt;
        if (prijaty_bajt !=KONCOVY_ZNAK) {
           aktual_index++;
           // ochrana proti preteceniu bufera
           if (aktual_index >=MAX_POCET_BAJTOV)  aktual_index = 0;
       }
       else {
           // v premennej aktual_index mas pocet priajtych bajtov
           sprava_ukoncena = TRUE;
           - zakaz prerusenia od T0
           return;
       }
    }
    // neprisiel stop bit
    else {
       fn_inicializacia_prijmu_bajtu();
       return;
    }
  }
  cnt_pom++;
}

void  fn_inicializacia_prijmu_bajtu(void)
{
        - nastav pin T0 ako vstupny
        - nastav do predvolby T0 0xff
        - nastav prerusenie od pretecenia T0
        prijaty_bajt = 0;
        cnt_pom = 0;
}

void  fn_spracuj_protokol(void)
{

  // co len chces
  // ....
  // koniec co len chces

  sprava_ukoncena = FALSE;
  fn_inicializacia_prijmu_bajtu();
  aktual_index = 0;
}

Dufam, ze som v druhom kode nenarobil nejake principialne chyby, treba to skontrolovat. Hlavne si vsimni, ze nikde ziadne cakacie slucky :slight_smile:
Obdobne naprogramujes i vysielanie. Na zaciatok prerusovacej rutiny si este hodis podmienku ci sa sprava prijima, alebo vysiela. Pri vysielani vyuzivas iba prerusenie od casovaca v periode casu 1Bd.
Prajem vela uspechov a daj vediet ze co a ako.