Context: Study at Beuth Hochschule, Embedded Systems with Prof. Dr.-Ing. Detlef Heinemann GSM moduleHardwareAT-commandsThe GSM module can be operated with AT-commands (see Wikipedia [1] ). A complete tutorial can be found here. The module can be configured, controlled and read out over the COM-port with the help of the AT-commands. A terminal program (e.g. Microsoft HyperTerminal, MTTTY) is used to send the commands. Due to the asynchronous communication via COM-port, both devices, PC and GSM module, have to be equally configured: Baud rate: 9600, 8-bit data word, no parity, one stop bit (Short: 9600,8,n,1) See below some command examples: AT The response "Ok" symbolizes a working connection between PC and module. AT+CPIN? Request, if a PIN is needed to access the device. Answer: +CPIN: <code> OK. <code> descript the state and can have the following meanings: READY: PIN was already entered or is not needed SIM PIN/PUK: PIN expected AT+CPIN=”1234?
Inserts PIN 1234. If the response is "Ok" was the PIN correct and the device can be configured. AT+CMGF=1 This important command sets the input and output of the GSM module to human readable text mode. Further operations are now easier. Response should be "Ok". AT+CMGL="ALL" Lists all SMS in the memory. AT+CMGL="REC UNREAD" Lists all unread SMS in the memory. Now all unread SMS are set to "REC READ". AT+CMGD=<index> Deletes the SMS with index <index> from the memory. ATD<number> Calls the number <number> and tries to establish a connection with the target (not used in this project). Project SMSConcept
The idea is to display an SMS on a LCD or LED ticker.
RealizationThe SMS is send via mobile net to the GSM module, is read out with a FPGA board and is displayed finally on a LCD/LED ticker.
VideoHardwareThe FPGA board is connected via the RS232 interface to the GSM module. Both boards have an integrated voltage level converter, which is adapting to the corresponding level. That's why both modules can be easily connected to the PC or to each other. The Nexys2 board communicates via AT-commands with the GSM module and is polling every second the current SMS in the memory. The GSM should send the SMS as human readable text to the board, which saves the SMS to a RAM. The LCD is operated with 8-bit data words and is displaying the SMS from the RAM as a ticker. This LCD module needs a negative voltage for the contrast. I use a MAX232 IC to generate the negative voltage level. This IC is normally used as a voltage level converter, but fortunately it outputs the supply voltage VCC as a negative voltage -V.
The MAX232 is placed in the middle of the bread board. The negative supply voltage -V can be found at pin 6. This is a easy way to generate negative voltages without having two power supplies. In this case I use 5V from the Nexys2 board as power supply for the MAX232. The negative voltage of -5V is regulated with a poti (LCD contrast needs 0V down to -5V) and supplied to the contrast pin of the LCD. MAX232 is not able to deliver high current, but it's enough as reference voltage. You can find the application note of the MAX232 datasheet below (ELKOs can be replaced with ceramic capacitors of 100nF). SchematicI/Os# CLOCK NET "CLK" CLOCK_DEDICATED_ROUTE = FALSE; NET "CLK" LOC="B8"; # RS232 NET "RXD" LOC = "U6"; NET "TXD" LOC = "P9"; #NET "test_txd" LOC = "M18"; # Output NET LED<0> LOC = J14; NET LED<1> LOC = J15; NET LED<2> LOC = K15; NET LED<3> LOC = K14; NET LED<4> LOC = E16; NET LED<5> LOC = P16; NET LED<6> LOC = E4; NET LED<7> LOC = P4; #RESET NET "RESET" LOC = "G18"; #FLAG NET "FLAG_R" LOC = "C17"; NET "FLAG_T" LOC = "J13"; NET "bit_clk" LOC = "G15"; #NET LED_OUT LOC = J14; ##FLAG #NET "LED_OUT" LOC = "C17"; #LCD NET DATA<0> LOC = L15; NET DATA<1> LOC = K12; NET DATA<2> LOC = L17; NET DATA<3> LOC = M15; NET DATA<4> LOC = K13; NET DATA<5> LOC = L16; NET DATA<6> LOC = M14; NET DATA<7> LOC = M16; NET ENA LOC = M13; NET RS LOC = R18; NET CLK_1000Hz LOC = R15; Modules
The following modules are realized in VHDL:
TX The transmitter:
Figure: Tx module
The transmitter communicates via RS232 protocol. In detail, it is the driver to send 8-bit data words over the RS232 interface of the FPGA board. The Nexys2 board already has a voltage level converter. The corresponding pins of the module are directly connected to this IC, therefore it's not necessary to consider neither voltage levels nor inverting of the data bits (Serial interface: voltage levels from -12V up to 12V; '1' equals -12V and '0' equals 12V). The TX module is sending with 9600 baud and the data format 8,n,1 (8 data bits, no parity, 1 stop bit). The baud rate is generated by the module using the 50MHz board clock. The data word is read parallel and sent serially. Starting the transmission process sets the transmission flag (Flag_T). The flag is reset after the correct transmission of the stop bit. The baud rate of 9600Hz can be used externally via pin "bit_clk". An asynchronous reset is resetting all states to their default values. library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity transmitter is Port ( CLK : in STD_LOGIC; TXD : out STD_LOGIC; bit_clk : out std_logic; RESET : in STD_LOGIC; DATA_IN : in STD_LOGIC_VECTOR (7 downto 0); FLAG_T : inout STD_LOGIC); end transmitter; architecture Behavioral of transmitter is constant baudDivide : std_logic_vector(9 downto 0) := "1010001011"; --baude rate divider for 9600 by 4*baude rate = sample rate -- by dividing 50Mhz by 2 and 38400 signal clkDiv : std_logic_vector(9 downto 0) := "0000000000"; signal tClk : std_logic := '0'; -- sample clock (38400) signal sendBuffer : std_logic_vector(7 downto 0); begin process (CLK) --Define rClk begin if CLK = '1' and CLK'Event then if clkDiv = baudDivide then tClk <= not tClk; clkDiv <= "0000000000"; else clkDiv <= clkDiv +1; end if; end if; end process; process(tClk,RESET) variable count : integer; begin if(RESET='1') then -- asynchrous reset FLAG_T <= '0'; count := 0; sendBuffer <= "00000000"; elsif(tClk'event and tClk='1' ) then if(FLAG_T='0')then if not(DATA_IN = "00000000") then FLAG_T <= '1' ; end if; count := 0; elsif(FLAG_T='1') then sendBuffer <= DATA_IN; case count is when 0 => TXD <= '0' ; bit_clk <= '1'; -- Start-Bit when 4 => TXD <= sendBuffer(0);bit_clk <= '1'; -- 6. Takt LSB when 8 => TXD <= sendBuffer(1);bit_clk <= '1'; -- 10. Takt when 12 => TXD <= sendBuffer(2);bit_clk <= '1'; -- 14. Takt when 16 => TXD <= sendBuffer(3);bit_clk <= '1'; -- 18. Takt when 20 => TXD <= sendBuffer(4);bit_clk <= '1'; -- 22. Takt when 24 => TXD <= sendBuffer(5);bit_clk <= '1'; -- 26. Takt when 28 => TXD <= sendBuffer(6);bit_clk <= '1'; -- 30. Takt when 32 => TXD <= sendBuffer(7);bit_clk <= '1'; -- 34. Takt MSB when 36 => TXD <= '1' ;bit_clk <= '1'; --stop-Bit when 60 => FLAG_T<='0' ; when others => bit_clk <= '0'; -- 38. Takt Stop-Bit end case ; count := count + 1; end if ; end if ; end process ; end Behavioral; RX
The receiver:
Figure: Rx module
The receiver module operates with the same specifications as the transmitter. The RX signal of the RS232 interface is observed by the module. If the signal is put to ground (start bit) and the RX module is ready (Receiver flag FLAG_R is reset), the incoming data bits are sensed and saved in a buffer (sensing rate equals four times the baud rate). It's important that the data bit is sensed in the middle to verify a stable state on the RS232 line. FLAG_R is set during the receiving process and reset after the stop bit was received correctly. The received data is output on a 8bit data bus and is buffered until the next receiving process. An asynchronous reset is resetting all states and the data buffer.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity receiver is Port ( CLK : in STD_LOGIC; RXD : in STD_LOGIC; FLAG_R : inout STD_LOGIC; DATA_OUT : out STD_LOGIC_VECTOR (7 downto 0); RESET : in STD_LOGIC); end receiver; architecture Behavioral of receiver is constant baudDivide : std_logic_vector(9 downto 0) := "1010001011"; --baude rate divider for 9600 by 4*baude rate = sample rate -- by dividing 50Mhz by 2 and 38400 signal clkDiv : std_logic_vector(9 downto 0) := "0000000000"; signal rClk : std_logic := '0'; -- sample clock (38400) signal readBuffer : std_logic_vector(7 downto 0); begin process (CLK) --Define rClk begin if CLK = '1' and CLK'Event then if clkDiv = baudDivide then rClk <= not rClk; clkDiv <= "0000000000"; else clkDiv <= clkDiv +1; end if; end if; end process; process(rClk,RESET) variable count : integer; begin if(RESET='1') then -- asynchrous reset FLAG_R <= '0' ; count := 0; DATA_OUT <= "00000000" ; elsif(rClk'event and rClk='1' ) then if(RXD='0' and FLAG_R='0')then -- Start-Bit FLAG_R <= '1' ; count := 0; elsif(FLAG_R='1') then case count is when 5 => readBuffer(0) <= RXD ; -- 6. Takt LSB when 9 => readBuffer(1) <= RXD ; -- 10. Takt when 13 => readBuffer(2) <= RXD ; -- 14. Takt when 17 => readBuffer(3) <= RXD ; -- 18. Takt when 21 => readBuffer(4) <= RXD ; -- 22. Takt when 25 => readBuffer(5) <= RXD ; -- 26. Takt when 29 => readBuffer(6) <= RXD ; -- 30. Takt when 33 => readBuffer(7) <= RXD ; -- 34. Takt MSB when 37 => FLAG_R <= '0' ; when others => null; end case ; count := count + 1; DATA_OUT <= readBuffer; end if ; end if ; end process ; end Behavioral; RAM
Figure: RAM module (Dual-Port-RAM)
The RAM is internal data storage for the SMS. Basically it's a 8bit wide RAM with 256 possible addresses. You can compare it to a char array of 256 chars. This RAM features parallel writing and reading of different storage addresses (separate address and data busses for read and write access: So called Dual-Port-RAM). Read and write processes are enabled with separated control signals. Access is synchronized with the board clock. An asynchronous reset clears all data cells with the value 0x01h.
library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity RAM is Port ( DATA_OUT : out STD_LOGIC_VECTOR (7 downto 0); DATA_IN : in STD_LOGIC_VECTOR (7 downto 0); ADDRESS : in STD_LOGIC_VECTOR (7 downto 0); ADDRESS_OUT : in STD_LOGIC_VECTOR (7 downto 0); RD : in STD_LOGIC; CLK : in STD_LOGIC; RESET : in STD_LOGIC; WRT : in STD_LOGIC); end RAM; architecture Behavioral of RAM is type arr is array(0 to 255) of std_logic_vector(7 downto 0); function vec_to_int (vec : in STD_LOGIC_VECTOR (7 downto 0)) return integer is variable result : integer := 0; begin result := 0; if (vec(7) = '1') then result := result + 128; end if; if (vec(6) = '1') then result := result + 64; end if; if (vec(5) = '1') then result := result + 32; end if; if (vec(4) = '1') then result := result + 16; end if; if (vec(3) = '1') then result := result + 8; end if; if (vec(2) = '1') then result := result + 4; end if; if (vec(1) = '1') then result := result + 2; end if; if (vec(0) = '1') then result := result + 1; end if; return result; end; begin process(CLK, RESET, ADDRESS) variable i : integer; variable data : arr; begin if (RESET = '1') then for i in 0 to 255 loop data(i) := "00000001"; end loop; DATA_OUT <= "00000001"; elsif (CLK'event and CLK = '1') then --else if (RD = '1') then --Read data DATA_OUT <= data(vec_to_int(ADDRESS_OUT)); end if; if (WRT = '1') then --Write data data(vec_to_int(ADDRESS)) := DATA_IN; end if; end if; end process; end Behavioral; LCD controller
The LCD controller is used to control the LCD and to display the SMS on the LCD like a ticker. First step is to initialize the display (8bit data, 1 line, cursor off, cursor moves, cursor increments). Now the data is read from the RAM and is displayed on the LCD. The controller is responsible for the right offset of the text to invoke the effect of scrolling letters. The LCD is filled multiple times a second and the starting address is incremented after each of these cycles. That's why the text seems to scroll through the display. This module is working independently of the RAM content. The module tries immediately after reboot to read data from the RAM although there is no data stored. Maybe I'll add a control signal from the system controller to fix this problem. library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity LCD_Controller is Port ( CLK : in STD_LOGIC; CLK_1000Hz : inout STD_LOGIC; RESET : in STD_LOGIC; DATA_OUT : out STD_LOGIC_VECTOR (7 downto 0); RAM_ADDRESS : out STD_LOGIC_VECTOR (7 downto 0); ENABLE : out STD_LOGIC; RD_RAM : out STD_LOGIC; RS : out STD_LOGIC; DATA_IN : in STD_LOGIC_VECTOR (7 downto 0)); end LCD_Controller; architecture Behavioral of LCD_Controller is type arr is array(0 to 36) of std_logic_vector(7 downto 0); signal CLK_1ms : std_logic; begin process(CLK) variable cnt : integer range 0 to 50000 := 0; begin if (CLK'Event) and (CLK = '1') then if (cnt >= 25000) then CLK_1ms <= not(CLK_1ms); CLK_1000Hz <= not(CLK_1000Hz); cnt := 0; end if; cnt := cnt + 1; end if; end process; process(CLK_1ms, RESET) variable token : arr; variable isInit : boolean := false; variable initStep : integer := 0; variable writeStep : integer := 0; variable cmd : std_logic_vector (7 downto 0); variable ram_add : std_logic_vector (7 downto 0); variable cntTime : integer := 0; variable cntLimit : integer := 0; variable letterOffset : integer := 0; begin if (RESET = '1') then isInit := false; --controll var for initilize display initStep := 0; --controll var for different commands cntTime := 0; --timer for waiting times cntLimit := 0; --timer limit writeStep := 0; --controll var for writing on display (which char) letterOffset := 0; --offset for shifting effect on display ram_add := "00000000"; --RAM address lines resetz RD_RAM <= '1'; --Read RAM allways 1 elsif (CLK_1ms'Event) and (CLK_1ms = '1') then RAM_ADDRESS <= ram_add; --setting RAM address if (isInit = false) then --initilize LCD if (cntTime = 0) then case initStep is -- define commands and waiting times when 0 => cmd := "00111000"; cntLimit := 100; --0x38 when 1 => cmd := "00111000"; cntLimit := 50; --0x38 when 2 => cmd := "00111000"; cntLimit := 10; --0x38 when 3 => cmd := "00111000"; cntLimit := 10; --0x38 when 4 => cmd := "00111000"; cntLimit := 10; --0x38 when 5 => cmd := "00000110"; cntLimit := 10; --0x06 when 6 => cmd := "00001100"; cntLimit := 10; --0x0C when 7 => cmd := "00010100"; cntLimit := 10; --0x14 when 8 => cmd := "00000001"; cntLimit := 50; --0x01 when 9 => cmd := "10000000"; cntLimit := 100; --0x10 when others => initStep := 0; isInit := true; end case; DATA_OUT <= cmd; --command ist outputted RS <= '0'; --writing command ENABLE <= '1'; --high stroke end if; cntTime := cntTime + 1; if (cntTime = 5) then ENABLE <= '0'; --end of stroke (4ms length) end if; if (cntTime >= cntLimit) then --waiting times cntTime := 0; --reset waiting time counter initStep := initStep + 1; --change between different commands end if; else --LCD is init if (cntTime = 0) then if (writeStep = 0) then --Reset cursor position cntLimit := 100; --prev 10, now for waiting for shift cmd := "10000000"; --Reset cursor position to 0x00 DATA_OUT <= cmd; --command ist outputted RS <= '0'; --writing command else if (DATA_IN = "00000001") then --empty data from RAM changed to <space> DATA_OUT <= "00100000"; else DATA_OUT <= DATA_IN; --Output data from RAM end if; RS <= '1'; --writing data cntLimit := 10; --define waiting time end if; ENABLE <= '1'; --high stroke end if; cntTime := cntTime + 1; if (cntTime = 5) then ENABLE <= '0'; --end of stroke (4ms length) end if; if (cntTime >= cntLimit) then --waiting time for LCD cntTime := 0; --reset counter for waiting writeStep := writeStep + 1; --writing next char ram_add := ram_add + 1; --next databyte from RAM if (writeStep > 20) then --end of display line writeStep := 0; --start from beginning letterOffset := letterOffset + 1; --Offset for shifting the LCD if (letterOffset >= 180) then letterOffset := 0; end if; ram_add := "00000000"; --reset RAM address ram_add := ram_add + letterOffset; --starting with offset for shifting effect on display end if; end if; end if; end if; end process; end Behavioral; System controller
The system controller is the central controlling device. It uses the RX and the TX component to communicate with the GSM module. The controller is sending the AT-command AT+CMGL="ALL" every second to poll the stored SMS from the GSM module. The received data is saved in the RAM. At the moment the GSM module has to be configured via PC (e.g. PIN, text mode, clear the memory), because the system controller is only capable of sending one command. It's planed to make the system work autonomously. Therefore it has to deal with all necessary commands. library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.STD_LOGIC_ARITH.ALL; use IEEE.STD_LOGIC_UNSIGNED.ALL; entity Test_Data_out is Port ( DATA_OUT : out STD_LOGIC_VECTOR (7 downto 0); FLAG_T : in std_logic; FLAG_R : in std_logic; RESET : in STD_LOGIC; DATA_IN : in STD_LOGIC_VECTOR (7 downto 0); RAM_OUT : out STD_LOGIC_VECTOR (7 downto 0); RAM_ADDRESS : out STD_LOGIC_VECTOR (7 downto 0); --RD_RAM : out std_logic; WRT_RAM : out std_logic; clk : in std_logic); end Test_Data_out; architecture Behavioral of Test_Data_out is type arr is array(0 to 13) of std_logic_vector(7 downto 0); --shared variable token : arr; signal at_cmgl_all : arr; begin process(clk, RESET) variable cnt : integer range 0 to 13 := 0; variable cnt_clk : integer range 0 to 100000000 := 90000000; variable send : boolean; variable ram_wrt : boolean; variable sending : boolean; variable ram_add : std_logic_vector (7 downto 0); procedure init_tokens is begin at_cmgl_all(0) <= "01100001"; --a at_cmgl_all(1) <= "01110100"; --t at_cmgl_all(2) <= "00101011"; --+ at_cmgl_all(3) <= "01100011"; --c at_cmgl_all(4) <= "01101101"; --m at_cmgl_all(5) <= "01100111"; --g at_cmgl_all(6) <= "01101100"; --l at_cmgl_all(7) <= "00111101"; --= at_cmgl_all(8) <= "00100010"; --" at_cmgl_all(9) <= "01100001"; --a at_cmgl_all(10) <= "01101100"; --l at_cmgl_all(11) <= "01101100"; --l at_cmgl_all(12) <= "00100010"; --" at_cmgl_all(13) <= "00001101"; --<cr> end init_tokens; begin init_tokens; if (RESET = '1') then ram_add := "00010100"; --20 for text start position cnt_clk := 0; elsif (clk = '1') and (clk'event) then if (cnt_clk = 100000000) then cnt_clk := 0; send := true; ram_add := "00010100"; --20 for text start position end if; if (send = true) then if (FLAG_T = '0') then DATA_OUT <= at_cmgl_all(cnt); end if; if (FLAG_T = '1') then sending := true; end if; if (FLAG_T = '0') and (sending = true) then sending := false; cnt := cnt + 1; end if; if (cnt = 14) then cnt := 0; send := false; DATA_OUT <= "00000000"; end if; else if (FLAG_R = '0') then ram_wrt := true; RAM_ADDRESS <= ram_add; RAM_OUT <= DATA_IN; WRT_RAM <= '1'; else if (ram_wrt = true) then ram_add := ram_add + 1; ram_wrt := false; end if; end if; end if; cnt_clk := cnt_clk + 1; end if; end process; end Behavioral; Comments Sources |
Projects > FPGA Projects >