Błąd powstaje w warunkach częstych zmian i częstych wyświetleń map bitowych.
Fragment kodu wywoływany klawiaturą (można trzymać wciśnięty klawisz, wtedy dzieje się to z dużą częstotliwością):
if (LcdChangeAllowed)
{
try
{
LcdDispAllowed = false;
int LCDCustomChar = LCDAddr >> 3;
int LCDCustomLine = LCDAddr & 7;
bool D;
bool[] LCDLineBits = new bool[5];
IntToBin(LCDWriteData, out D, out D, out D, out LCDLineBits[0], out LCDLineBits[1], out LCDLineBits[2], out LCDLineBits[3], out LCDLineBits[4]);
for (int i = 0; i < 5; i++)
{
if (LCDLineBits[i])
{
DrawLCDPixel(0, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(0, 0, 0));
DrawLCDPixel(1, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(255, 255, 255));
if (LCDCustomLine < 7)
{
DrawLCDPixel(2, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(0, 0, 0));
DrawLCDPixel(3, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(255, 255, 255));
}
else
{
DrawLCDPixel(2, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(0, 0, 0));
DrawLCDPixel(3, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(0, 0, 0));
}
}
else
{
DrawLCDPixel(0, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(255, 255, 255));
DrawLCDPixel(1, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(128, 128, 128));
if (LCDCustomLine < 7)
{
DrawLCDPixel(2, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(255, 255, 255));
DrawLCDPixel(3, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(128, 128, 128));
}
else
{
DrawLCDPixel(2, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(0, 0, 0));
DrawLCDPixel(3, LCDCustomChar, i, LCDCustomLine, Color.FromArgb(0, 0, 0));
}
}
}
}
catch
{
MessageBox.Show("Zmiana");
}
LcdDispAllowed = true;
}
Treść metody DrawLcdPixel, LCDDisp to tablica bitmap
private void DrawLCDPixel(int Nr, int Chr, int X, int Y, Color Clr)
{
bool NotDone = true;
while(NotDone)
{
try
{
LCDDisp[Nr, Chr].SetPixel(X * 2 + 0, Y * 2 + 0, Clr);
LCDDisp[Nr, Chr].SetPixel(X * 2 + 1, Y * 2 + 0, Clr);
LCDDisp[Nr, Chr].SetPixel(X * 2 + 0, Y * 2 + 1, Clr);
LCDDisp[Nr, Chr].SetPixel(X * 2 + 1, Y * 2 + 1, Clr);
LCDDisp[Nr, Chr + 8].SetPixel(X * 2 + 0, Y * 2 + 0, Clr);
LCDDisp[Nr, Chr + 8].SetPixel(X * 2 + 1, Y * 2 + 0, Clr);
LCDDisp[Nr, Chr + 8].SetPixel(X * 2 + 0, Y * 2 + 1, Clr);
LCDDisp[Nr, Chr + 8].SetPixel(X * 2 + 1, Y * 2 + 1, Clr);
NotDone = false;
}
catch
{
NotDone = true;
MessageBox.Show("DrawLCDPixel");
}
}
}
Zmienne LcdChangeAllowed i LcdDispAllowed są typu bool i mają zapewnić uniknięcie próby wykonywania obu tych fragmentów naraz
Drugi fragment, wywoływany z timera, im mniejszy interwał timera tym większe prawdopodobnieństwo błędu.
if (LcdDispAllowed)
{
LcdChangeAllowed = false;
try
{
LCD00.Image = LCDDisp[L0Cur[0], L0Disp[0]];
LCD01.Image = LCDDisp[L0Cur[1], L0Disp[1]];
LCD02.Image = LCDDisp[L0Cur[2], L0Disp[2]];
LCD03.Image = LCDDisp[L0Cur[3], L0Disp[3]];
LCD04.Image = LCDDisp[L0Cur[4], L0Disp[4]];
LCD05.Image = LCDDisp[L0Cur[5], L0Disp[5]];
LCD06.Image = LCDDisp[L0Cur[6], L0Disp[6]];
LCD07.Image = LCDDisp[L0Cur[7], L0Disp[7]];
LCD08.Image = LCDDisp[L0Cur[8], L0Disp[8]];
LCD09.Image = LCDDisp[L0Cur[9], L0Disp[9]];
LCD0A.Image = LCDDisp[L0Cur[10], L0Disp[10]];
LCD0B.Image = LCDDisp[L0Cur[11], L0Disp[11]];
LCD0C.Image = LCDDisp[L0Cur[12], L0Disp[12]];
LCD0D.Image = LCDDisp[L0Cur[13], L0Disp[13]];
LCD0E.Image = LCDDisp[L0Cur[14], L0Disp[14]];
LCD0F.Image = LCDDisp[L0Cur[15], L0Disp[15]];
LCD10.Image = LCDDisp[L1Cur[0], L1Disp[0]];
LCD11.Image = LCDDisp[L1Cur[1], L1Disp[1]];
LCD12.Image = LCDDisp[L1Cur[2], L1Disp[2]];
LCD13.Image = LCDDisp[L1Cur[3], L1Disp[3]];
LCD14.Image = LCDDisp[L1Cur[4], L1Disp[4]];
LCD15.Image = LCDDisp[L1Cur[5], L1Disp[5]];
LCD16.Image = LCDDisp[L1Cur[6], L1Disp[6]];
LCD17.Image = LCDDisp[L1Cur[7], L1Disp[7]];
LCD18.Image = LCDDisp[L1Cur[8], L1Disp[8]];
LCD19.Image = LCDDisp[L1Cur[9], L1Disp[9]];
LCD1A.Image = LCDDisp[L1Cur[10], L1Disp[10]];
LCD1B.Image = LCDDisp[L1Cur[11], L1Disp[11]];
LCD1C.Image = LCDDisp[L1Cur[12], L1Disp[12]];
LCD1D.Image = LCDDisp[L1Cur[13], L1Disp[13]];
LCD1E.Image = LCDDisp[L1Cur[14], L1Disp[14]];
LCD1F.Image = LCDDisp[L1Cur[15], L1Disp[15]];
}
catch
{
MessageBox.Show("Wyswietlanie");
}
LcdChangeAllowed = true;
}
Według kodu, wszystko wydaje sie być w porządku, jest zabezpieczenie przed próbą jednoczesnego wykonania (zmienne LcdChangeAllowed i LcdDispAllowed), a także jest zapewniona obsługa błędu poprzez wyświetlenie komunikatu z odpowiednią (try/catch, w catch jest MessageBox.Show).
Jak widać, wszystko jest w porządku, ale po odpaleniu i wystąpieniu błędu nie pojawia się żaden z komunikatów ShowMessage, a pojawia się taki ekran:
Jeżeli jeden z przedstawionych fragmentów kodu wyłączę z działania poprzez zamianę na komentarz, to obojętnie, który z tych dwóch fragmentów wyłączę, wtedy błędu nie ma, tylko, że oba fragmenty są potrzebne.
Normalnie, program jest wielowątkowy, ale przewidziałem w nim wyłączenie wielowątkowości. Obie przedstawione metody wykonują się w osobnych wątkach. Jeżeli jest ta wielowątkowość, to wymieniony problem występuje. A jeżeli jest jednowątkowość (bez wielowątkowości), to problemu nie ma.
Z tego, co zauważyłem, w całej tej sprawie chodzi o dostęp do tablicy LCDDisp przed dwie metody naraz.
Tylko śmieszne jest to, że tylko jeden z tych dwóch fragmentów modyfikuje tą tablicę, a drugi z nich tylko ją czyta.
W przypadku pierwszego fragmentu (ten, który zapisuje do tablicy LCDDisp), ten kod MUSI być wykonany, a jeżeli LCDDisp jest zablokowany i kod nie może być wykonany, to program ma poczekać, aż LCDDisp się zwolni i wykonać kod.
Co do drugiego fragmentu, w którym jest tylko czytanie LCDDisp, to jeżeli czytania nie da się zrealizować, to wykonywanie kodu może zostać pominięte, a może być wykonany później, w tym przypadku to bez różnicy.
Gdzie szukać przyczyny i jak rozwiązać problem?