Ahoj,poradil by mě někdo jak si udělat na 51 softwarový sér port? Už jsem něco našel na atmel.com/dyn/resources/prod … 2ec329.pdf ale moc tomu nerozumím a nejradši bych to chtěl napsat v Céčku.
koukám sice že je to na pic, ale ta teorie je na 51 určitě stejná. Koukal jsem na ten program:
void _Soft_USART_Write(unsigned short chr) {
unsigned short mask=1,i;
unsigned int data;
data = chr << 1;
data &= ~0x0001; // START bit (=0).
// 10 output bits
for (i=0;i<9;i++) { // lsb 1st 8 bits
if (mask & data) {
asm {CLRW}
SU_set; // output low for logic 1
} else {
SU_res;// output high for logic 0
asm {CLRW}
}
mask <<=1;
delay_us(395); // 2400 simulate to calibrate the loop.
}
SU_set; // stop bit
delay_us(417); // 2400
}
a myslím si že ho celkem chápu. Prostě vezmu data a masku a navzájem to 8x rotuju a pak posílám 1 nebo 0,ale není mě jasné co dělá to
asm{CLRW}
a
delay_us(395);
myslím že jedno bude ještě něco s tím odesláním na port a druhé nastavení rychlosti přenosu???
Omg. Kdyby to nemelo byt stejny proc bych Ti to posilal? CLRW je instrukce nulovani watchdogu myslim. A Delay Ti asi dava zpozdeni nebo delku jednoho bitu v seriovem prenosu. Jeho delkou nastavis baudrate.
Ten reset watchdogu bych z funkce vyhodil, nulovat po kazdym odeslanym bitu je docela neefektivni, resetoval bych ho nekde pred volanim funkce…
já to naprogramoval takhle, ale zatím netuším jestli to vůbec bude fungovat. Potřeboval jsem komunikaci oběma směry.
/* ========================================================================== */
/* ser data cteni */
/* ========================================================================== */
ser_data_cteni:
maska2=0x01; // 0000 0001
P3|=0x00;
if(P3==0x40)
{
cteni=1;
}
if(cteni==1)
{
cekej(1,5bitu) //cekej 1,5 bitu
for(ii=0;ii<8;ii++)
{
if(P3==0x40)
{
data_in |= maska2;
cekej(1bit); // 1bit
}
else
{
data_in &=0xff;
}
}
maska2 << 1;
cteni=0;
cekej(0,5bitu)
}
/* ========================================================================== */
/* ser data zapis */
/* ========================================================================== */
ser_data_zapis:
int data_out;
int maska=0x80;
int ii;
P3&=0xDF; // START bit =0
cekej(1bit); //cekej 1bit
for (ii=0;ii<9;ii++)
{
if (maska & data_out)
{
P3|=0x20; // vystup log 1
}
else
{
P3&=0xDF; // vystup log 0
}
data_out = data_out << 1;
cekej(1bit); //cekej 1bit
}
P3|=0x20; // vystup log 1 -> stop bit = 1
cekej(1bit); //cekej 1bit
s tím že fce cekej(cislo) je dána přesně přerušením od čítače/časovače a zbejvá nastavit na nejmenší krok a pak přesně dopočítat časy aby to komunikovalo 38400 bit/s.
P.S.: to nahoře anonimní je můj dotaz
Zdravim,
nechcem moc cerit tiche vody typu “delay_us”, ale prave taketo riesenia
dehonestuju vybavu hw periferii jednocipakov.
Myslim, ze vacsinou sa od procesora ocakava, ze ak komunikuje, to automaticky neznamena, ze nema robit nic ineho (AD prevody, filtracia signalu, zobrazovanie na LCD, interakcia s tlacitkami, vypocty regulacnych sluciek, atd, atd). Obzvlast, ak caka na prijem nejakej spravy.
SW uart moze byt napisany tak ako bolo uvedene vyssie, ale ak je to mozne, za systemovejsie riesenie povazujem vyuzit HW vybavu procesora.
Konkretne na x51 (ale aj na ine typy) je dobre pouzit interupt od casovaca.
Predpokladam, ze MCU najprv caka na nejaku spravu od PC a potom na nu odpoveda, komunikacia typu 8N1.
Prijem:
Vstupny pin casovaca T vyuzijem na RX. 8b casovac nastavim do modu pocitania vstupnych impulzov a do pocitadla mu vlozim hodnotu 0xff. Prichodom dobeznej hrany na pine casovac pretecie a vyhodi prerusenie. Nie je teda potrebne obetovat na RX nejaky iny pin s funkciou INT.
V tomto preruseni zmenim mod citaca/casovaca na casovac a prednastavim cas na 1/2 Bd rychlosti. Samozrejme s prerusenim.
Po druhom preruseni otestujem, ci je vstupny signal v log.0. Ak ano, udalost prehlasim za platny start bit. Do predvolby nastavim cas 1 Bd rychlosti.
Postupne pride 8 preruseni. V kazdom z nich otestujem hodnotu pinu procesora (vstup pre T) a ulozim do nejakeho bajtu ako platne bity.
Pockam na deviate prerusenie. Ak je hodnota pinu 1, jedna sa o platny stop bit, prijaty bajt mozem ulozit do komunikacneho bufera - akoby mi prislo prerusenie od UARTu a celu cinnost opakujem.
Vysielanie:
Ten isty citac / casovac, ktory som pouzival pri prijme vyuzijem na odpocet vyslania bajtu. Predpokladam komunikaciu typu poloduplex.
Ak komunikujem cez budic RS485, prepnem smer na vysielanie (iny pin ako RX a TX.)
Pin procesora (iny ako som pouzil na RX, ale v zasade lubovolny) nastavim na log.0 (start bit), do predvolby vlozim cas 1 Bd rychlosti. Postupne pride 8 preruseni. V kazdom z nich nastavim na vystup prislusny bit z vysielaneho bajtu. Po deviatom preruseni nastavim pin na log.1 - stop bit. Ak komunikujem cez RS 232, cinnost ukoncim/pokracujem v odvysielani dalsieho bajtu, ak cez RS485, pockam na desiate prerusenie a otocim smer budica RS485 na prijem, ak uz nie je potrebne odvysielat dalsi bajt.
Ak komunikujem rychlostou 38400/9600Bd, potom cas medzi jednotlivymi bitmy je cca 26/104us.Osobne robim s ATmega (14.7456MHz) a tie za tu dobu urobia 383/1533 instrukcii. A to je naozaj dost dlha doba, aby sa procesor len tak flakal v nejakej zapraskanej casovej slucke. Okrem toho C prekladac program optimalizuje a bezduche casove slucky (ak nie su asm volatile) optimalizuje, pripadne cele vyhadzuje. s tym maju hlavne zaciatocnici psychicke problemy typu - ved som to tam napisal, tak preco to ten “blby” prekladac vyhadzuje a upravuje. No preto, lebo to nie je ASM a od prekladaca primarne chceme, aby nase programatorske “blaboly” optimalizoval do hustejsieho a rychlejsieho kodu. A pri rozne zapnutych optimalizaciach budu prirodzene casove/velkostne vysledky rozne.
Preto celkovo v C doporucujem sa funkciam typu delay_ms/us vyhybat (aj ked principialne sa pouzit daju, akurat brzdia cely procesor) a naucit sa vyuzivat ine prostriedky, ktore su na casove funkcie v jednocipoch urcene.
Horeuvedeny postup je uplne nezavisly, ci programujem v ASM, alebo v C.
Takže abych vše upřesnil. Zapojení vypadá takto:
mám tam připojený krystal 12MHz. Ale popravdě moc nevím jak to zapracovat do toho čítače časovače.Něco jsem napsal,ale jestli je to ok to nevím,zkusím to vyzkoušet.[code]
/* ========================================================================== /
/ ser data cteni /
/ ========================================================================== /
void cteni ()
{
mikros=0;
while(mikros==39){ //dokud neubehne 1,5 bitu vrat (39 mikrosekund)
//nop(); // nic nedelej
}
for(ii=0;ii<8;ii++)
{
mikros=0;
if(P3==0x40)
{
data_in |= maska2;
}
else
{
data_in &=0xff;
}
while(mikros==26){ //dokud neubehne 1 bit vrat (26 mikrosekund)
//nop();
}
maska2 << 1;
}
while(mikros==13){ //dokud neubehne 0,5 bitu vrat (13 mikrosekund)
//nop(); // nic nedelej
}
}
/ ========================================================================== /
/ ser data zapis /
/ ========================================================================== /
void zapis ()
{
maska=0x80;
P3&=0xDF; // START bit =0
mikros=0;
while(mikros==26){ //dokud neubehne 1 bit vrat (26 mikrosekund)
//nop(); // nic nedelej
}
for (ii=0;ii<9;ii++)
{
if (maska & data_out)
{
P3|=0x20; // vystup log 1
}
else
{
P3&=0xDF; // vystup log 0
}
data_out = data_out << 1;
while(mikros==26){ //dokud neubehne 1 bit vrat (26 mikrosekund)
//nop(); // nic nedelej
}
mikros=0;
}
P3|=0x20; // vystup log 1 → stop bit = 1
while(mikros==26){ //dokud neubehne 1 bit vrat (26 mikrosekund)
//nop(); // nic nedelej
}
}
/ ========================================================================== /
/ funkce preruseni /
/ ========================================================================== */
void timer0(void) interrupt 1
{
static int i;
if(i<1000){ //casova smycka 1ms
i++;
}
else
{
ms++;
i=0;
}
mikros++; // pocita mikro sekundy >> jeden krok citace/casovace
ser_data_cteni:
maska2=0x01; // 0000 0001
P3|=0x00;
if(P3==0x40)
{
cteni();
}
ser_data_zapis:
if(data_out!=0){
zapis();
}
return;
}
/* ========================================================================== /
/ HLAVNI FUNKCE /
/ ========================================================================== /
void main()
{
/ ========================================================================== /
/ nastaveni citace /
/ ========================================================================== */
TMOD = 0x02;
TH0 = 256-1;
TR0 = 1;
ET0 = 1;
EA = 1;
while(1)
{
}[/code]
administrator: příspěvek byl upraven
Externí soubor byl uploadnut na server.
Asi ani neskusaj
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.
- 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 , 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
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.
Moc pěkný postup, děkuji. Ale já myslel že bych tyto výstupy využíval normálně jako např bránu P2. a k tomu používal normální 8 bit čítač/čas co udělá vnitřní přerušení.Popravdě vůbec nevím jak udělat přerušení na T0.
poradil by mě někdo jak nastavit 16 bit čitač/časovač?aby např. každých 150 mikrosekund vytvořil přerušení.Krystal je 12MHz.
mám nastavený 8bit takto a funguje super, takže bych potřeboval podle stejného postupu i ten druhý,ale nějak se v návodu strácím:
void timer0(void) interrupt 1
{
zde je obsluha preruseni
}
void main()
{
TMOD = 0x02;
TH0 = 256-100;
TR0 = 1;
ET0 = 1;
EA = 1;
}
V tom Ti neporadim - robim s inym mcu. Na to su tu povolanejsi
Mělo by to být nějak takto:
TMOD = 0x22; //č/č 0 i 1 v modu 8bit RELOAD
TH0 = 256-100;
TL0 = 256-100;
TR0 = 1;
ET0 = 1;
TH1 = 256-150;
TL1 = 256-100;
TR1 = 1;
ET1 = 1;
EA = 1;
Jinak pokud máš problémy se čtením cizojazyčných návodů,
tak si kup knihu “Práce s mikrokontroléry ATMEL …” z nakladatelství BEN.
Mají jich tam několik, stačí si vybrat.